From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Serge Petrenko Message-Id: <305E5CD8-68DD-4897-9E77-347F290091A6@tarantool.org> Content-Type: multipart/alternative; boundary="Apple-Mail=_0EF187BC-EBA2-4A2B-9BE0-7231D22AE127" Mime-Version: 1.0 (Mac OS X Mail 11.5 \(3445.9.1\)) Subject: Re: [tarantool-patches] [PATCH v2] Introduce separate entity object types for entity privileges. Date: Tue, 14 Aug 2018 16:41:25 +0300 In-Reply-To: <20180807163842.agnqqk7gyua2vwzv@esperanza> References: <20180802105558.20488-1-sergepetrenko@tarantool.org> <20180807163842.agnqqk7gyua2vwzv@esperanza> To: Vladimir Davydov Cc: tarantool-patches@freelists.org, kostja@tarantool.org List-ID: --Apple-Mail=_0EF187BC-EBA2-4A2B-9BE0-7231D22AE127 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 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 =D0=B0=D0=B2=D0=B3. 2018 =D0=B3., =D0=B2 19:38, Vladimir Davydov = =D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BB(=D0=B0= ): >=20 > 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. >>=20 >> Closes: #3574 >> Prerequisite: #3524 >> --- >> = https://github.com/tarantool/tarantool/tree/sergepetrenko/gh-3574-whole-en= tity-types >> https://github.com/tarantool/tarantool/issues/3574 >>=20 >> Changes in v2:=20 >> - 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. >=20 > Hmm, I wonder what happens if someone creates an object (say space) = and > names it "*=C2=BB =E2=80=A6 It is ok, when operating with a space named =E2=80=98*=E2=80=99, =E2=80=98= *=E2=80=99 is in object_name, when operating on a whole entity, =E2=80=98*=E2=80=99 is set internally in object_id = field. So there are no conflicts here. >=20 >> - keep the new entity types in priv_def and use them >> internally. >>=20 >> 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(-) >>=20 >> 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) >> } >> } >>=20 >> +void >> +priv_def_try_fill(struct priv_def *priv, struct tuple *tuple) >=20 > 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(). >=20 I removed the function. >> +{ >> + const char *object_id =3D tuple_field(tuple, = BOX_PRIV_FIELD_OBJECT_ID); >> + if (object_id =3D=3D NULL) { >> + tnt_raise(ClientError, ER_NO_SUCH_FIELD, >> + BOX_PRIV_FIELD_OBJECT_ID + TUPLE_INDEX_BASE); >> + } >=20 > Please use tuple_field_u32() and tuple_field_str() instead. >=20 >> + /* >> + * 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) =3D=3D MP_STR) { >=20 > Shouldn't you check that the string is actually "*=C2=BB? Added a check >=20 >> + priv->object_id =3D 0; >> + priv->object_type =3D = schema_entity_type(priv->object_type); >=20 > 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. >=20 Done. >> + } else if (mp_typeof(*object_id) =3D=3D MP_UINT) { >> + priv->object_id =3D 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 =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_UID); >> const char *object_type =3D >> tuple_field_cstr_xc(tuple, BOX_PRIV_FIELD_OBJECT_TYPE); >> - priv->object_id =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_OBJECT_ID); >> priv->object_type =3D schema_object_type(object_type); >> + priv_def_try_fill(priv, tuple); >> if (priv->object_type =3D=3D 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 =3D=3D 'space' then >> - if object_name =3D=3D nil or object_name =3D=3D 0 then >> - return 0 >> - end >> + if object_name =3D=3D nil then >> + return '*' >> + end >=20 > Bad indentation - we use spaces in Lua code, not tabs. >=20 Fixed. >> local space =3D box.space[object_name] >> if space =3D=3D 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 =3D=3D 'function' then >> - if object_name =3D=3D nil or object_name =3D=3D 0 then >> - return 0 >> - end >> + if object_name =3D=3D nil then >> + return '*' >> + end >=20 > Ditto Fixed >=20 >> local _vfunc =3D box.space[box.schema.VFUNC_ID] >> local func >> if type(object_name) =3D=3D 'string' then >> @@ -1836,8 +1836,8 @@ local function object_resolve(object_type, = object_name) >> end >> end >> if object_type =3D=3D 'sequence' then >> - if object_name =3D=3D nil or object_name =3D=3D 0 then >> - return 0 >> + if object_name =3D=3D nil then >> + return '*' >> end >> local seq =3D sequence_resolve(object_name) >> if seq =3D=3D nil then >> @@ -1846,6 +1846,9 @@ local function object_resolve(object_type, = object_name) >> return seq >> end >> if object_type =3D=3D 'role' then >> + if object_name =3D=3D nil then >> + return '*' >> + end >=20 > Ditto. >=20 Fixed >> local _vuser =3D box.space[box.schema.VUSER_ID] >> local role >> if type(object_name) =3D=3D 'string' then >> @@ -1867,6 +1870,9 @@ local function object_name(object_type, = object_id) >> if object_type =3D=3D 'universe' then >> return "" >> end >> + if object_id =3D=3D '*' then >> + return '*' >> + end >> local space >> if object_type =3D=3D 'space' then >> space =3D box.space._vspace >> @@ -2079,9 +2085,8 @@ local function grant(uid, name, privilege, = object_type, >> object_name =3D privilege >> privilege =3D 'execute' >> end >> - local privilege_hex =3D privilege_check(privilege, object_type) >> - >> local oid =3D object_resolve(object_type, object_name) >> + local privilege_hex =3D privilege_check(privilege, object_type) >=20 > I don't understand why you need this. Removed, no need to do this. >=20 >> options =3D options or {} >> if options.grantor =3D=3D nil then >> options.grantor =3D 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 =3D=3D '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 '*') >=20 > 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. >=20 If I set object_name to =E2=80=98*=E2=80=99 the case of granting to an = entity as a whole will Be indistinguishable from granting to an object named =E2=80=98*=E2=80=99.= 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 =3D privilege >> privilege =3D 'execute' >> end >> - local privilege_hex =3D privilege_check(privilege, object_type) >> options =3D options or {} >> local oid =3D object_resolve(object_type, object_name) >> + local privilege_hex =3D privilege_check(privilege, object_type) >=20 > I don't understand why you need this. Removed. >=20 >> local _priv =3D box.space[box.schema.PRIV_ID] >> local _vpriv =3D box.space[box.schema.VPRIV_ID] >> local tuple =3D _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 =3D=3D '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 =3D 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 '*') >=20 > 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. >=20 >> end >> if privilege_hex ~=3D 0 then >> _priv:replace{grantor, uid, object_type, oid, privilege_hex} >> else >> _priv:delete{uid, object_type, oid} >> end >> + >=20 > Extraneous change. Please remove. Done >=20 >> end >>=20 >> 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 =3D tuple[4] ~=3D '*' and tuple[4] or nil >> + box.session.su("admin", revoke, uid, uid, tuple[5], = tuple[3], oid) >=20 > Why? The 'revoke' function should be able to interpret '*' correctly. >=20 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 >>=20 >> = +-------------------------------------------------------------------------= ------- >> +--- Tarantool 1.10.1 >> = +-------------------------------------------------------------------------= ------- >> +local function upgrade_space_priv_to_1_10_1() >> + local _priv =3D box.space._priv >> + local _vpriv =3D box.space._vpriv >> + local f =3D _priv:format() >> + >> + f[4].type =3D 'scalar' >> + _priv:format(f) >> + f =3D _vpriv:format() >> + f[4].type =3D 'scalar' >> + _vpriv:format(f) >> + _priv.index.primary:alter{parts=3D{2, 'unsigned', 3, 'string', = 4, 'scalar'}} >> + _vpriv.index.primary:alter{parts=3D{2, 'unsigned', 3, 'string', = 4, 'scalar'}} >> + _priv.index.object:alter{parts=3D{3, 'string', 4, 'scalar'}} >> + _vpriv.index.object:alter{parts=3D{3, 'string', 4, 'scalar'}} >> +end >> + >> +local function upgrade_privs_to_1_10_1() >=20 > No need to split this code in two functions. >=20 >> + local _priv =3D box.space._priv >> + >> + for _, priv in _priv:pairs() do >=20 > 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. >=20 >> + if priv[4] =3D=3D 0 then >> + if priv[3] ~=3D 'universe' and priv[3] ~=3D 'user' and = priv[3] ~=3D 'role' then >=20 > Please rewrite it as >=20 > if priv[4] =3D=3D 0 and priv[3] ~=3D 'universe' and ... then >=20 >> + _priv:delete{priv[2], priv[3], priv[4]} >> + _priv:insert{priv[1], priv[2], priv[3], '*', priv[5]} >=20 > Bad indentation - we use spaces in Lua code, not tabs. >=20 Removed the privilege upgrade function together with some leftover code = handling=20 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 >>=20 >> local function get_version() >> local version =3D box.space._schema:get{'version'} >> @@ -991,6 +1027,7 @@ local function upgrade(options) >> {version =3D mkversion(1, 7, 6), func =3D upgrade_to_1_7_6, = auto =3D false}, >> {version =3D mkversion(1, 7, 7), func =3D upgrade_to_1_7_7, = auto =3D true}, >> {version =3D mkversion(1, 10, 0), func =3D upgrade_to_1_10_0, = auto =3D true}, >> + {version =3D mkversion(1, 10, 1), func =3D = upgrade_to_1_10_1, auto =3D true}, >> } >>=20 >> 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"; >=20 > Shouldn't it be "*=C2=BB? 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=20 >> + * fails even before the upgrade script is being = run. >> + * Same applies to SC_FUNCTION and SC_SEQUENCE = below. >> + */ >> if (object_id =3D=3D 0) >> return "SPACE"; >> struct space *space =3D 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; >> + } >> } >>=20 >> #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" >>=20 >> static const char *object_type_strs[] =3D { >> - /* [SC_UKNNOWN] =3D */ "unknown", >> - /* [SC_UNIVERSE] =3D */ "universe", >> - /* [SC_SPACE] =3D */ "space", >> - /* [SC_FUNCTION] =3D */ "function", >> - /* [SC_USER] =3D */ "user", >> - /* [SC_ROLE] =3D */ "role", >> - /* [SC_SEQUENCE] =3D */ "sequence", >> - /* [SC_COLLATION] =3D */ "collation", >> + /* [SC_UKNNOWN] =3D */ "unknown", >> + /* [SC_UNIVERSE] =3D */ "universe", >> + /* [SC_SPACE] =3D */ "space", >> + /* [SC_ENTITY_SPACE] =3D */ "all spaces", >> + /* [SC_FUNCTION] =3D */ "function", >> + /* [SC_ENTITY_FUNCTION] =3D */ "all functions", >> + /* [SC_USER] =3D */ "user", >> + /* [SC_ENTITY_USER] =3D */ "all users", >> + /* [SC_ROLE] =3D */ "role", >> + /* [SC_ENTITY_ROLE] =3D */ "all roles", >> + /* [SC_SEQUENCE] =3D */ "sequence", >> + /* [SC_ENTITY_SEQUENCE] =3D */ "all sequences", >> + /* [SC_COLLATION] =3D */ "collation", >> + /* [SC_ENTITY_COLLATION] =3D */ "all collations", >=20 > Why? I don't think that you need to report "all spaces" anywhere. fixed. Here=E2=80=99s 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 =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_ID); priv->grantee_id =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_UID); + const char *object_type =3D tuple_field_cstr_xc(tuple, BOX_PRIV_FIELD_OBJECT_TYPE); - priv->object_id =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_OBJECT_ID); priv->object_type =3D schema_object_type(object_type); + + const char *object_id =3D tuple_field(tuple, = BOX_PRIV_FIELD_OBJECT_ID); + if (object_id =3D=3D 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) =3D=3D MP_STR) { + object_id =3D tuple_field_cstr_xc(tuple, = BOX_PRIV_FIELD_OBJECT_ID); + if(*object_id =3D=3D '*' && strlen(object_id) =3D=3D 1) = { + priv->object_id =3D 0; + priv->object_type =3D = 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) =3D=3D MP_UINT) { + priv->object_id =3D 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 =3D=3D 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..da6961d1be7621fe9821623c7c2d5712= 5e8ac7bc 100644 GIT binary patch delta 1552 zcmV+r2JiWV43!L!7=3DJM>GBYwQXE`%5Ib$*h}EFr~oj`FvARU1MnJQi4e@o&wmVLh3G~crY-}4Qt)1A zB2#HdNhp@x{7t9y{=3D-^iy?G~GRT*HEED-ei@Dn^G44RYT!B-?xN&&+Fx&Y1qV@|wy z6m_Jn@7Frx341NeT$e$x{G;bbkGWq_9B~MwrUdvJKu^0mC)0U%u6xqfb3Hyc=3DuW3S z?Yy*~VIJ-(N`EEV@r?j@mYTMGX?c^oS(oYGod@w`90IW?o~7muA2}mX0M&<$IO}p( zf4&sYv(&5wsva}nU)$;MEH#TN5iEy0m!_+prRGbuYQ0okQ!15=3DOOySbmm=3DQXK9owS5|kbsI5jm z@5t{w$A3eAY2$e)R8?lXx9syH+_Gd3aW42YQzE)T8_E)F-H zZMkTx4a(%@NY`j|3&7;fLG#dK@AP_j^} zP;!~^J6oZkGC}22X(6$YN~9HQM=3DJH^bEJ+Wj3U%Z@+>t|M)c-`lf>C_fV+XIPfer%Uc*!XHsf;ldcAyrKaq~ zTz|`(6uRQYTqhKE{4`zxF)J9%3;Y*e!9Y}Om3pCY>tRqbsThdDE*Ta}CiN~&>g}76 z!4jk9%y>A~28QcJ%EhV0$D*{Nl2mw>nl_~poi;_)WA0d&u^?M6 zNt2TTsE)CLA*jJzWK0dr004jhC;$-#M=3D54i4-$aDIEte%3SbZhL(DFTh=3Dt}nh{on7iA*E0z$QhnjhC8 zCtYIjZW*~2>%o=3D401{W2UfNK%SuRO#7z?U1OHmr$_k9#!4DKOfeHdDiLMUMhE_CY3 zQ}wM+3}S$xrp{sO0%D-Qg!Tg?N`D#c#11D%H1uMldybk2C_2GIa1@o`AvlV1g6&6a zBm#;e0oq=3DkL|+Xk(;`Axi4jzulb=3DM-vXNUm0-}edv^J)qU^;iT zG&tlm=3D>j9yIEcC(gPg(fIe%Gp29&-BccznI$Z20Zp2%7_$W9>{;D>@m9ppFRLi`BZ zxQFm2G$}GSoq&fM>MYyVZ?D8K%fMRllKy8xF*61u0i|dk(Um!gfT;c_$vzr)bR`hg z8QidjRzibA!Kym`2j>)8MFY}}Gzpe5HBi$3iFF|fX;^3K2nxO;&dT4F*z~{Np5p=3DVQyn(Iv_b=3DV_`LAF*YqTV`gS8= zG-hTvEnzrjFfCy?W;Qf4VP!dGGGq!?Lu_wjYdRo%F*+bOeF_TIx(m9^1&9F7A|rpb zr2qf`001bpFZ}>e{VMI&<{eU?~Ai$VOI~v629dx2JaIm@kx4N&&zCxB$xlBK}6v zM#}nreKVfmYggvF>;bcooE<&pjzw|&ydgCd;B)Xi4C|Up=3DiRyONm;Mu=3Dn31mId~$~K1OP2OJJrGIzc!y|v*5Q}mxHFpG|K|%#jeQ^A;E^l>* z3*Bp}NefgxX1+gm^KmUTgDMc1Jsb?P)oZC4!mL!?OgGcvU!7uNP_0u4V%4c@$$Tde zOCDy|bcHe0;hLW1g*=3D6kJQqvOo9n=3D`!@rT1v3PA=3DsEaGtUmrgF1V*+HwF$= z%o|ck2Ck)MDdN~U>lpNk)Rh9)Qd8p>$FU`TvVVQ}JNw3+pEy`&7|(v!cb#wKcU}Xc zztr*E^M9!-1a3aZxQyRo-mQ)X;JxvQVS5a1E-Z#;*X8(mp!hlb(b=3DWn_TxWHa?e&p z0ms_NyVg8C=3DMtXRLX=3DhZO}2jlujiL*MCw|r<8%iFyl-urCG}bk_A!)lFN(Z z*9rxd2`ZdQ%ZOz}8m(9>QmHeYBvm9q6rtA9wbTrW=3DuAh+mpZa8Z9M*WuhEnWw8OPP z0Ken?`duE+M1Fc0sWuO;rRJ>TPnWxUKDEZdwbU$UUQcC(VtJFR2G>&aT`%TX-lUJ$ zzkkQk8h`!+Ak42Gb5O~I9X_GL4xhR(RGq@`<8YEmeGLBa%Ed{YaV#4wH7X6(wXrob zRx(mCOfa|?Bo}qm;#z9jPz5?Ii>k-mKV61Bw$iw6MNm)>lUj2vHAVFK{#e#!kBV!l ziDGG`ab1f2WXaFUg5t{}`TM8d6EBXt_J3uuRg$)9X&lOg&M*+4wU)S+njl=3D^il77Rj$ynhqj zxeG%W18Ui|uoin^FOJH&6BE>1-;h#Us!zr2@yPWOP}$xk=3D>TS*Ej6Q8ZgBx&}sXh}Wym z;Yvs=3DXm6s&oTIkS5VXc28)t^2+kc4qR4r1#;*gdpU~^0s$pG8pm?G)gVGer7p5*j@ zrQSOro_cGm&fR}<@b8h^=3DiXW!%4reE=3DRqJFWsqt`1BcS0HC@As3ba#z^Ko9Pi8avYk$Oa!!S#& zSx3_!A8xyrpfT#3Y0zk_Hi}Y>p%$pI61F+5M3N&%2xKJAC;$CDD}<6?=3D$#5uoTZ5Z zCNj}EgB|fKv=3D$L@ag>MEJ_76DnN*W1Z`O`MZ9ptwx|K1yxMV1V1(Ltx%(4y7obk4E zW?=3D^8GIQ=3Dkv4A0`wS7N1*>`Z!opukPx!_g@c{8|RJi<`!QS!z#<&+X#nnV8V6uo^% zFL^|hfwSZ-+|LAZ&d5i?Zt>-$tMH_1UnT!Yb{@SussOv@WfJ50JyfLCQd9Bw_N-dh k2=3DV>Mm|$S01}gnuSl5OyKJnJxL~`9Mur5?b8(5ga7~l 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 =20 local function object_resolve(object_type, object_name) + if object_name ~=3D nil and type(object_name) ~=3D 'string' + and type(object_name) ~=3D 'number' then + box.error(box.error.ILLEGAL_PARAMS, "wrong object name type") + end if object_type =3D=3D 'universe' then - if object_name ~=3D nil and type(object_name) ~=3D 'string' - and type(object_name) ~=3D 'number' then - box.error(box.error.ILLEGAL_PARAMS, "wrong object name = type") - end return 0 end + if (object_type =3D=3D 'space' or object_type =3D=3D 'function' or + object_type =3D=3D 'sequence' or object_type =3D=3D 'role') and + object_name =3D=3D nil then + return '*' + end if object_type =3D=3D 'space' then - if object_name =3D=3D nil or object_name =3D=3D 0 then - return 0 - end local space =3D box.space[object_name] if space =3D=3D 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 =3D=3D 'function' then - if object_name =3D=3D nil or object_name =3D=3D 0 then - return 0 - end local _vfunc =3D box.space[box.schema.VFUNC_ID] local func if type(object_name) =3D=3D 'string' then @@ -1836,9 +1835,6 @@ local function object_resolve(object_type, = object_name) end end if object_type =3D=3D 'sequence' then - if object_name =3D=3D nil or object_name =3D=3D 0 then - return 0 - end local seq =3D sequence_resolve(object_name) if seq =3D=3D 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 =3D=3D 'universe' then return "" end + if object_id =3D=3D '*' then + return '*' + end local space if object_type =3D=3D 'space' then space =3D 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 =3D=3D '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 =3D=3D '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 =3D 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 ~=3D 0 then _priv:replace{grantor, uid, object_type, oid, privilege_hex} else _priv:delete{uid, object_type, oid} end + end =20 local function drop(uid, opts) @@ -2192,9 +2192,10 @@ local function drop(uid, opts) local privs =3D _vpriv.index.primary:select{uid} =20 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 =3D tuple[4] ~=3D '*' 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 =20 = +-------------------------------------------------------------------------= ------- +--- Tarantool 1.10.1 = +-------------------------------------------------------------------------= ------- +local function upgrade_space_priv_to_1_10_1() + local _priv =3D box.space._priv + local _vpriv =3D box.space._vpriv + local f =3D _priv:format() + + f[4].type =3D 'scalar' + _priv:format(f) + f =3D _vpriv:format() + f[4].type =3D 'scalar' + _vpriv:format(f) + _priv.index.primary:alter{parts=3D{2, 'unsigned', 3, 'string', 4, = 'scalar'}} + _vpriv.index.primary:alter{parts=3D{2, 'unsigned', 3, 'string', 4, = 'scalar'}} + _priv.index.object:alter{parts=3D{3, 'string', 4, 'scalar'}} + _vpriv.index.object:alter{parts=3D{3, 'string', 4, 'scalar'}} +end + +local function upgrade_to_1_10_1() + upgrade_space_priv_to_1_10_1() +end =20 local function get_version() local version =3D box.space._schema:get{'version'} @@ -991,6 +1013,7 @@ local function upgrade(options) {version =3D mkversion(1, 7, 6), func =3D upgrade_to_1_7_6, = auto =3D false}, {version =3D mkversion(1, 7, 7), func =3D upgrade_to_1_7_7, = auto =3D true}, {version =3D mkversion(1, 10, 0), func =3D upgrade_to_1_10_0, = auto =3D true}, + {version =3D mkversion(1, 10, 1), func =3D upgrade_to_1_10_1, = auto =3D true}, } =20 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 =3D=3D 0) - return "SPACE"; struct space *space =3D space_by_id(object_id); if (space =3D=3D NULL) break; @@ -547,8 +555,6 @@ schema_find_name(enum schema_object_type type, = uint32_t object_id) } case SC_FUNCTION: { - if (object_id =3D=3D 0) - return "FUNCTION"; struct func *func =3D func_by_id(object_id); if (func =3D=3D NULL) break; @@ -556,8 +562,6 @@ schema_find_name(enum schema_object_type type, = uint32_t object_id) } case SC_SEQUENCE: { - if (object_id =3D=3D 0) - return "SEQUENCE"; struct sequence *seq =3D = sequence_by_id(object_id); if (seq =3D=3D 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; + } } =20 #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" =20 static const char *object_type_strs[] =3D { - /* [SC_UKNNOWN] =3D */ "unknown", - /* [SC_UNIVERSE] =3D */ "universe", - /* [SC_SPACE] =3D */ "space", - /* [SC_FUNCTION] =3D */ "function", - /* [SC_USER] =3D */ "user", - /* [SC_ROLE] =3D */ "role", - /* [SC_SEQUENCE] =3D */ "sequence", - /* [SC_COLLATION] =3D */ "collation", + /* [SC_UKNNOWN] =3D */ "unknown", + /* [SC_UNIVERSE] =3D */ "universe", + /* [SC_SPACE] =3D */ "space", + /* [SC_ENTITY_SPACE] =3D */ "space", + /* [SC_FUNCTION] =3D */ "function", + /* [SC_ENTITY_FUNCTION] =3D */ "function", + /* [SC_USER] =3D */ "user", + /* [SC_ENTITY_USER] =3D */ "user", + /* [SC_ROLE] =3D */ "role", + /* [SC_ENTITY_ROLE] =3D */ "role", + /* [SC_SEQUENCE] =3D */ "sequence", + /* [SC_ENTITY_SEQUENCE] =3D */ "sequence", + /* [SC_COLLATION] =3D */ "collation", + /* [SC_ENTITY_COLLATION] =3D */ "collation", }; =20 +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 =3D SC_NEW_OBJECT + 1 + * schema_entity_type() relies on this convention. */ enum schema_object_type { SC_UNKNOWN =3D 0, SC_UNIVERSE =3D 1, SC_SPACE =3D 2, - SC_FUNCTION =3D 3, - SC_USER =3D 4, - SC_ROLE =3D 5, - SC_SEQUENCE =3D 6, - SC_COLLATION =3D 7, - schema_object_type_MAX =3D 8 + SC_ENTITY_SPACE =3D 3, + SC_FUNCTION =3D 4, + SC_ENTITY_FUNCTION =3D 5, + SC_USER =3D 6, + SC_ENTITY_USER =3D 7, + SC_ROLE =3D 8, + SC_ENTITY_ROLE =3D 9, + SC_SEQUENCE =3D 10, + SC_ENTITY_SEQUENCE =3D 11, + SC_COLLATION =3D 12, + SC_ENTITY_COLLATION =3D 13, + schema_object_type_MAX =3D 13 }; =20 +/** + * 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); =20 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 =3D universe.access; break; } + case SC_ENTITY_SPACE: + { + access =3D entity_access.space; + break; + } + case SC_ENTITY_FUNCTION: + { + access =3D entity_access.function; + break; + } + case SC_ENTITY_SEQUENCE: + { + access =3D entity_access.sequence; + break; + } case SC_SPACE: { - if (priv->object_id =3D=3D 0) { - access =3D entity_access.space; - break; - } struct space *space =3D space_by_id(priv->object_id); if (space) access =3D space->access; @@ -220,10 +231,6 @@ access_find(struct priv_def *priv) } case SC_FUNCTION: { - if (priv->object_id =3D=3D 0) { - access =3D entity_access.function; - break; - } struct func *func =3D func_by_id(priv->object_id); if (func) access =3D func->access; @@ -231,10 +238,6 @@ access_find(struct priv_def *priv) } case SC_SEQUENCE: { - if (priv->object_id =3D=3D 0) { - access =3D entity_access.sequence; - break; - } struct sequence *seq =3D = sequence_by_id(priv->object_id); if (seq) access =3D 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 =3D 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 =3D 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 = =3D 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']]] --=20 2.15.2 (Apple Git-101.1) --Apple-Mail=_0EF187BC-EBA2-4A2B-9BE0-7231D22AE127 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8 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 =D0=B0=D0=B2=D0=B3. = 2018 =D0=B3., =D0=B2 19:38, Vladimir Davydov <vdavydov.dev@gmail.com> =D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0= =B0=D0=BB(=D0=B0):

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-35= 74-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 "*=C2=BB =E2=80=A6
It is ok, when operating with a space named = =E2=80=98*=E2=80=99, =E2=80=98*=E2=80=99 is in object_name, when = operating
on a whole entity, =E2=80=98*=E2=80=99 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 =3D tuple_field(tuple, BOX_PRIV_FIELD_OBJECT_ID);
+ = if (object_id =3D=3D 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) =3D=3D = MP_STR) {

Shouldn't you check = that the string is actually "*=C2=BB?

Added a check


+ priv->object_id =3D 0;
+ = = priv->object_type =3D = 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) = =3D=3D MP_UINT) {
+ priv->object_id =3D = 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 =3D = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_UID);
const = char *object_type =3D
tuple_field_cstr_xc(tuple, = BOX_PRIV_FIELD_OBJECT_TYPE);
- priv->object_id =3D = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_OBJECT_ID);
= priv->object_type =3D schema_object_type(object_type);
+ = priv_def_try_fill(priv, tuple);
if = (priv->object_type =3D=3D 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 =3D=3D 'space' then
-        if object_name =3D=3D nil or = object_name =3D=3D 0 then
-         =    return 0
-        end
+ = if object_name =3D=3D nil then
+ =     return '*'
+ end

Bad indentation - we use spaces = in Lua code, not tabs.


Fixed.

  =       local space =3D box.space[object_name]
        if  space =3D=3D 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 =3D=3D 'function' = then
-        if object_name =3D=3D = nil or object_name =3D=3D 0 then
-       =      return 0
-       =  end
+ if object_name =3D=3D nil then
+ =     return '*'
+ end

Ditto

Fixed


  =       local _vfunc =3D box.space[box.schema.VFUNC_ID]
        local func
  =       if type(object_name) =3D=3D 'string' then
@@ -1836,8 +1836,8 @@ local function = object_resolve(object_type, object_name)
    =     end
    end
  =   if object_type =3D=3D 'sequence' then
-   =      if object_name =3D=3D nil or object_name =3D=3D 0 = then
-            return = 0
+        if object_name =3D=3D nil = then
+            return = '*'
        end
  =       local seq =3D sequence_resolve(object_name)
        if seq =3D=3D nil then
@@ -1846,6 +1846,9 @@ local function = object_resolve(object_type, object_name)
    =     return seq
    end
    if object_type =3D=3D 'role' then
+ = if object_name =3D=3D nil then
+ =     return '*'
+ end

Ditto.


Fixed

        local = _vuser =3D box.space[box.schema.VUSER_ID]
    =     local role
        if = type(object_name) =3D=3D 'string' then
@@ -1867,6 +1870,9 = @@ local function object_name(object_type, object_id)
 =   if object_type =3D=3D 'universe' then
  =       return ""
    end
+    if object_id =3D=3D '*' then
+ =        return '*'
+   =  end
    local space
  =   if object_type =3D=3D 'space' then
    =     space =3D box.space._vspace
@@ -2079,9 = +2085,8 @@ local function grant(uid, name, privilege, object_type,
        object_name =3D privilege
        privilege =3D 'execute'
    end
-    local = privilege_hex =3D privilege_check(privilege, object_type)
-
    local oid =3D = object_resolve(object_type, object_name)
+   =  local privilege_hex =3D privilege_check(privilege, object_type)

I don't understand why you need = this.

Removed, no need to do = this.

  =   options =3D options or {}
    if = options.grantor =3D=3D nil then
      =   options.grantor =3D 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 =3D=3D '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 =E2=80=98*=E2= =80=99 the case of granting to an entity as a whole will
Be = indistinguishable from granting to an object named =E2=80=98*=E2=80=99. = 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 =3D privilege
        privilege =3D 'execute'
    end
-    local = privilege_hex =3D privilege_check(privilege, object_type)
    options =3D options or {}
  =   local oid =3D object_resolve(object_type, object_name)
+    local privilege_hex =3D = privilege_check(privilege, object_type)

I don't understand why you need this.

Removed.

    local _priv =3D = box.space[box.schema.PRIV_ID]
    local _vpriv =3D= box.space[box.schema.VPRIV_ID]
    local tuple = =3D _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 =3D=3D '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 =3D 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 ~=3D 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 =3D tuple[4] ~=3D '*' 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 = =3D box.space._priv
+    local _vpriv =3D = box.space._vpriv
+    local f =3D = _priv:format()
+
+    f[4].type =3D = 'scalar'
+    _priv:format(f)
+ =    f =3D _vpriv:format()
+   =  f[4].type =3D 'scalar'
+   =  _vpriv:format(f)
+   =  _priv.index.primary:alter{parts=3D{2, 'unsigned', 3, 'string', 4, = 'scalar'}}
+   =  _vpriv.index.primary:alter{parts=3D{2, 'unsigned', 3, 'string', 4, = 'scalar'}}
+   =  _priv.index.object:alter{parts=3D{3, 'string', 4, 'scalar'}}
+    _vpriv.index.object:alter{parts=3D{3, = 'string', 4, 'scalar'}}
+end
+
+local function upgrade_privs_to_1_10_1()

No need to split this code in two = functions.

+ =    local _priv =3D 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] =3D=3D 0 then
+ =            if priv[3] ~=3D 'universe' and = priv[3] ~=3D 'user' and priv[3] ~=3D 'role' then

Please rewrite it as

 if priv[4] =3D=3D 0 and priv[3] ~=3D = '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 =3D box.space._schema:get{'version'}
@@ = -991,6 +1027,7 @@ local function upgrade(options)
  =       {version =3D mkversion(1, 7, 6), func =3D = upgrade_to_1_7_6, auto =3D false},
      =   {version =3D mkversion(1, 7, 7), func =3D upgrade_to_1_7_7, auto = =3D true},
        {version =3D = mkversion(1, 10, 0), func =3D upgrade_to_1_10_0, auto =3D true},
+        {version =3D mkversion(1, 10, = 1), func =3D upgrade_to_1_10_1, auto =3D 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 "*=C2=BB?
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 =3D=3D 0)
= = = = return "SPACE";
struct space *space =3D = 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[] =3D {
- = /* [SC_UKNNOWN]         =3D */ "unknown",
- = /* [SC_UNIVERSE]        =3D */ "universe",
- = /* [SC_SPACE]           =3D */ = "space",
- /* [SC_FUNCTION]     =    =3D */ "function",
- /* = [SC_USER]            =3D */ "user",
- = /* [SC_ROLE]            =3D */ = "role",
- /* [SC_SEQUENCE]     =    =3D */ "sequence",
- /* = [SC_COLLATION]       =3D */ "collation",
+ = /* [SC_UKNNOWN]     =3D */ = "unknown",
+ /* [SC_UNIVERSE] =     =3D */ "universe",
+ /* = [SC_SPACE] = =     =3D */ "space",
+ /* = [SC_ENTITY_SPACE]=     =3D */ "all spaces",
+ = /* [SC_FUNCTION]     =3D */ = "function",
+ /* [SC_ENTITY_FUNCTION] =     =3D */ "all functions",
+ /* = [SC_USER] = =     =3D */ "user",
+ /* = [SC_ENTITY_USER] =     =3D */ "all users",
+ /* = [SC_ROLE] = =     =3D */ "role",
+ /* = [SC_ENTITY_ROLE] =     =3D */ "all roles",
+ /* = [SC_SEQUENCE] =     =3D */ "sequence",
+ /* = [SC_ENTITY_SEQUENCE]     =3D */ "all = sequences",
+ /* [SC_COLLATION] =     =3D */ "collation",
+ /* = [SC_ENTITY_COLLATION]    =3D */ "all collations",

Why? I don't think that you need = to report "all spaces" anywhere.

fixed.

Here=E2=80=99s 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 =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_ID);
  priv->grantee_id =3D = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_UID);
+
 = const char *object_type =3D
  = tuple_field_cstr_xc(tuple, BOX_PRIV_FIELD_OBJECT_TYPE);
- = priv->object_id =3D tuple_field_u32_xc(tuple, = BOX_PRIV_FIELD_OBJECT_ID);
  = priv->object_type =3D schema_object_type(object_type);
+
+ const char *object_id =3D = tuple_field(tuple, BOX_PRIV_FIELD_OBJECT_ID);
+ if = (object_id =3D=3D 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) =3D=3D MP_STR) {
+ = object_id =3D tuple_field_cstr_xc(tuple, = BOX_PRIV_FIELD_OBJECT_ID);
+ if(*object_id =3D=3D '*' = && strlen(object_id) =3D=3D 1) {
+ = priv->object_id =3D 0;
+ = priv->object_type =3D = 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) = =3D=3D MP_UINT) {
+ priv->object_id =3D = 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 =3D=3D 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..da6961d1be7621fe9821623c7c2d5712= 5e8ac7bc 100644
GIT binary patch
delta = 1552
zcmV+r2JiWV43!L!7=3DJM>GBYwQXE`%5Ib$<nWMc|RZgX^DZewLSAZ0N= (H8nRmV=3DXXd
zF*hwVWjJFkVlgvjEihy=3DV`616Vl-qkHVRflY;R+0Iv{&}3JTS_3%bn(n= gGty8d0aE
z0000004TLD{QyvnEC6=3D>*h}EFr~oj`FvARU1MnJQi4e@o&wmVLh3G= ~crY-}4Qt)1A
zB2#HdNhp@x{7t9y{=3D-^iy?G~GRT*HEED-ei@Dn^G44RYT!B-?xN&&= ;+Fx&Y1qV@|wy
z6m_Jn@7Frx341NeT$e$x{G;bbkGWq_9B~MwrUdvJKu^0mC)0U%u6xqfb3Hyc=3D= uW3S
z?Yy*~VIJ-(N`EEV@r?j@mYTMGX?c^oS(oYGod@w`90IW?o~7muA2}mX0M&= <$IO}p(
zf4&sYv(&5wsva}nU)$;MEH#TN5iEy0m!_+prRGbuYQ<Q1#lkEYO= 69^Y)ISUiiUp=3Dt
zC>0okQ!15=3DOOySbmm=3DQXK9owS5|kbsI5j<pFj+#A<bTA{p5xH= }*!A7AT`g}iJZp;q
zZa*(m3p`8BSuZbcKQF_xf+#w$E@QP&S5FE&OU<%B*MW5z3(smw@= be+qmpn7j&(6bi
z4t2HV0FPP-q?QbLmYSt_Yv-+BSSX~f6!0uH^?h+1Um~{s3}1ZqeLFvQuFf=3D|= {j%>m
z@5t{w$A3eAY2$e)R8<J*e6DdD-_^ohB@MWH-}BP;7}LqF7(2TzM;yY$&= ;#@t$W%_kH
z9$%7&x;hfzS!$A(2E;oVI=3D}4a<;%;?_$pE>?lXx9syH+_Gd3aW= 42YQzE)T8_E)F-H
zZMkTx<ymTaw;U~*R7{GMf&o{c3QV2A(*~c@i+>4a(%@NY`j|3&am= p;7;fLG#dK@AP_j^}
zP;!~^J6oZkGC}22X(6$YN~9HQM=3DJH^bEJ+Wj3U%Z@+>t|M)c-`<Vzb= _mpY#R-E$PB
z68(HF5MbZ$e*LnL=3DOE!wjv*E2foG|C>lf>C_fV+XIPfer%Uc*!XHsf= ;ldcAyrKaq~
zTz|`(6uRQYTqhKE{4`zxF)J9%3;Y*e!9Y}Om3pCY>tRqbsThdDE*Ta}CiN~= &>g}76
z!4jk9%y>A~28QcJ%EhV0$D*{Nl2mw>nl_~poi;_)WA0d&u^?M<= ;U+bo*s7Oh*@hmk*
z^!ff;)@2Y1&r<Wm(u(_9C-##iKP!s~oPR~~_fYj}UL1E0XYo~%x@vJB= SVCtS?4Grf
z@GLby$YL|n#zmT&8l1t!jMuTP=3DpcEc^R-#cNb1fwio7!{#Iv85xz4|oo= t5G~aD->6
zNt2TTsE)CLA*jJzWK0dr004jhC;$-#M=3D54i4-$aDIEte%3SbZhL<l$vP8= 0xu;D10^
zK@mhP7zBVEle%IQ1m-ef91jM-02ly+*uYMhP>(DFTh=3Dt}nh{on7iA*E0z= $QhnjhC8
zCtYIjZW*~2>%o=3D401{W2UfNK%SuRO#7z?U1OHmr$_k9#!4DKOfeHdDiLM= UMhE_CY3
zQ}wM+3}S$xrp{sO0%D-Qg!Tg?N`D#c#11D%H1uMldybk2C_2GIa1@o`AvlV1g6= &6a
zBm#;e0oq=3DkL|+Xk(;`Axi4jzulb=3DM-vXNUm0-}edv^<XF;UF|bEL;aG= O3HyUXd&f5
z88k6w(AXZb_cg2b20{u-Z}C<nz{iAARh?|geYYKQ#VFi+XhmiG4;^%v=3Dz= qu$=3D*R1g
zez1GMebM(LHVMWg)roWz-LV|EY6+@Ey-NL+1I(uM&?2Z6(jNJW%xscWl(`= {?w{V+H
z{3j}V(4Y_$d?5u8Nb<Kvi(X_xgWx*i-)Jo|L*giI$9)9W!7HhLCvO%PO>= ;J)qU^;iT
zG&tlm=3D>j9yIEcC(gPg(fIe%Gp29&-BccznI$Z20Zp2%7_$W9&= gt;{;D>@m9ppFRLi`BZ
zxQFm2G$}GSoq&fM>MYyVZ?D8K%fMRllKy8xF*61u0i|dk(Um!gfT;c_= $vzr)bR`hg
z8QidjRzibA!Kym`2j>)8MFY}}Gzpe5HBi$3iFF|fX;^3K2nxO;&<= T@dg3AHb5UuS&
CN52^W

delta = 1534
zcmV<a1p)e%41^4j7=3DJJ=3DG%zh^V>dT4F*z~{Np5p=3DVQyn(Iv_b=3D= V_`LAF*YqTV`gS8
zG-hTvEnzrjFfCy?W;Qf4VP!dGGGq!?Lu_wjYdRo%F*+bOeF_TIx(m9^1&9= F7A|rpb
zr2qf`001bpFZ}>e{VM<lJ=3DRLl7I6XqUp$Ch%_7lYJ_Lj_@PFoXP~+w= e(A76EO7~<&
z)F3;RA|;uhw(`4WgTRw>I&<{eU?~Ai$VOI~v629dx2JaIm@kx4N&= amp;&zCxB$xlBK}6v
zM#}nreKVfmYggvF>;bcooE<&pjzw|&ydgCd;B)Xi4C|Up=3D= iRyONm;Mu=3D<J|49rmyj
z!~XSg?^aVP(0>n31mId~$~K1OP2OJJrGIzc!y|v*5Q}mxHFpG|K|%#jeQ^A= ;E^l>*
z3*Bp}NefgxX1+gm^KmUTgDMc1Jsb?P)oZC4!mL!?OgGcvU!7uNP_0u4V%4c@$$= Tde
z<JJuBKo=3D+bJ1@oWHsew%rA$(CR4!bhCq$aiEJ3j}Cx2ttc)M<~yh-n= @9Ru2aUZxgY
zOU+j=3DF>OCDy|bcHe0;hLW1g*=3D6kJQqvOo9n=3D`!@rT1v3PA=3DsEaG= tUmrgF1V*+HwF$
z%o|ck2Ck)MDdN~U>lpNk)Rh9)Qd8p>$FU`TvVVQ}JNw3+pEy`&7|= (v!cb#wKcU}Xc
zztr*E^M9!-1a3aZxQyRo-mQ)X;JxvQVS5a1E-Z#;*X8(mp!hlb(b=3DWn_TxWH= a?e&p
z0<NVdiD5jvlVdHR&M*6U`SOA@wvN<F<4PlXstt_|t42g+g<= ;yeLeOP%|b(q;~x1wEl
zEj90~M>ms_NyVg8C=3DMtXRLX=3DhZO}2jlujiL*MCw|r<8%iFyl-urC= G}bk_A!)lFN(Z
z*9rxd2`ZdQ%ZOz}8m(9>QmHeYBvm9q6rtA9wbTrW=3DuAh+mpZa8Z9M*Wuh= EnWw8OPP
z0Ken?`duE+M1Fc0sWuO;rRJ>TPnWxUKDEZdwbU$UUQcC(VtJFR2G>&am= p;aT`%TX-lUJ$
zzkkQk8h`!+Ak42Gb5O~I9X_GL4xhR(RGq@`<8YEmeGLBa%Ed{YaV#4wH7X6= (wXrob
zRx(mCOfa|?Bo}qm;#z9jPz5?Ii>k-mKV61Bw$iw6MNm)>lUj2vHAVFK{= #e#!kBV!l
ziDGG`ab1f2WXaFUg5t{}`TM8d6EBXt_J3uuRg$)9X&lOg&M*+4wU)S= +njl=3D^i<EJZ
zX2ppwLyQ}LBXiMF<cgb*y<$jG_{C2oetkYX_VcpV`IoM<QW}RMaV&= lt;4zXt3a^V}T&3
z(OhIq3CsWhfB+}}AqGb&W<?JYfWSD4qc93!7zRWLI0_9I0RX{)ke~&g= t;l77Rj$ynhqj
zxeG%W18Ui|uoin^FOJH&6BE><l!R4e54%@5i^3tJ2=3DRjuhoRy}= $qP!PWxP;CvBe2+
zB`AQ$m7GU4)NS@llAFhZ>1-;h#Us!zr2@yPWOP}$xk=3D>TS*Ej6Q8Zg= Bx&}sXh}Wym
z;Yvs=3DXm6s&oTIkS5VXc28)t^2+kc4qR4r1#;*gdpU~^0s$pG8pm?G)gV= Ger7p5*j@
zrQSOro_cGm&fR}<@b8h^=3DiXW!%4reE=3DRqJFW<g443Z&4= $8`^`Azzc12c=3Dp(Su(w)T
z1)jmCmtK6UOn@l~=3DHxoFllhK1;>sqt`1BcS0HC@As3ba#z^Ko9Pi8avYk= $Oa!!S#&
zSx3_!A8xyrpfT#3Y0zk_Hi}Y>p%$pI61F+5M3N&%2xKJAC;$CDD}<= ;6?=3D$#5uoTZ5Z
zCNj}EgB|fKv=3D$L@ag>MEJ_76DnN*W1Z`O`MZ9ptwx|K1yxMV1V1(Ltx%(= 4y7obk4E
zW?=3D^8GIQ=3Dkv4A0`wS7N1*>`Z!opukPx!_g@c{8|RJi<`!QS!z#&l= t;&+X#nnV8V6uo^%
zFL^|hfwSZ-+|LAZ&d5i?Zt>-$tMH_1UnT!Yb{@SussOv@WfJ50JyfLC= Qd9Bw_N-dh
k2=3DV>Mm|$S01}gnuSl5<E4okR>OyKJnJxL~`9Mur5?b8(5ga7~l<= br class=3D"">
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 ~=3D nil and type(object_name) ~=3D = 'string'
+          =   and type(object_name) ~=3D 'number' then
+ =       box.error(box.error.ILLEGAL_PARAMS, "wrong = object name type")
+    end
     if object_type =3D=3D 'universe' then
-        if object_name ~=3D nil and = type(object_name) ~=3D 'string'
-      =           and type(object_name) ~=3D = 'number' then
-          =   box.error(box.error.ILLEGAL_PARAMS, "wrong object name = type")
-        end
         return 0
 =    end
+    if (object_type =3D=3D= 'space' or object_type =3D=3D 'function' or
+   =     object_type =3D=3D 'sequence' or object_type =3D=3D = 'role') and
+       object_name = =3D=3D nil then
+        return = '*'
+    end
    =  if object_type =3D=3D 'space' then
-    =     if object_name =3D=3D nil or object_name =3D=3D 0 = then
-          =   return 0
-      =   end
         local = space =3D box.space[object_name]
      =    if  space =3D=3D 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 =3D=3D = 'function' then
-        if = object_name =3D=3D nil or object_name =3D=3D 0 then
-  =           return 0
-  =       end
        =  local _vfunc =3D box.space[box.schema.VFUNC_ID]
 =        local func
    =      if type(object_name) =3D=3D 'string' then
@@ -1836,9 +1835,6 @@ local function = object_resolve(object_type, object_name)
    =      end
     end
     if object_type =3D=3D 'sequence' then
-        if object_name =3D=3D nil = or object_name =3D=3D 0 then
-        =     return 0
-      =   end
         local = seq =3D sequence_resolve(object_name)
      =    if seq =3D=3D 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 =3D=3D 'universe' then
      =    return ""
     end
+    if object_id =3D=3D '*' then
+        return '*'
+    end
    =  local space
     if object_type =3D=3D = 'space' then
         space =3D = 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 =3D=3D '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 =3D=3D = '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 =3D 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 ~=3D 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 =3D _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 =3D tuple[4] ~=3D = '*' 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 =3D box.space._priv
+    local _vpriv = =3D box.space._vpriv
+    local f =3D = _priv:format()
+
+  =   f[4].type =3D 'scalar'
+  =   _priv:format(f)
+    f =3D = _vpriv:format()
+    f[4].type =3D = 'scalar'
+    _vpriv:format(f)
+    _priv.index.primary:alter{parts=3D{2, = 'unsigned', 3, 'string', 4, 'scalar'}}
+  =   _vpriv.index.primary:alter{parts=3D{2, 'unsigned', 3, = 'string', 4, 'scalar'}}
+  =   _priv.index.object:alter{parts=3D{3, 'string', 4, = 'scalar'}}
+  =   _vpriv.index.object:alter{parts=3D{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 =3D = box.space._schema:get{'version'}
@@ -991,6 +1013,7 @@ = local function upgrade(options)
      =    {version =3D mkversion(1, 7, 6), func =3D upgrade_to_1_7_6, = auto =3D false},
        =  {version =3D mkversion(1, 7, 7), func =3D upgrade_to_1_7_7, auto =3D= true},
         {version =3D = mkversion(1, 10, 0), func =3D upgrade_to_1_10_0, auto =3D true},
+        {version =3D mkversion(1, = 10, 1), func =3D upgrade_to_1_10_1, auto =3D 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 =3D=3D= 0)
- return = "SPACE";
  struct space = *space =3D space_by_id(object_id);
  = if (space =3D=3D NULL)
  = break;
@@ -547,8 +555,6 @@ schema_find_name(enum = schema_object_type type, uint32_t object_id)
  = }
  case SC_FUNCTION:
 = {
- if (object_id =3D=3D= 0)
- return = "FUNCTION";
  struct func *func = =3D func_by_id(object_id);
  = if (func =3D=3D NULL)
  = break;
@@ -556,8 +562,6 @@ schema_find_name(enum = schema_object_type type, uint32_t object_id)
  = }
  case SC_SEQUENCE:
 = {
- if (object_id =3D=3D= 0)
- return = "SEQUENCE";
  struct sequence = *seq =3D sequence_by_id(object_id);
  = if (seq =3D=3D 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[] =3D {
- /* = [SC_UKNNOWN]         =3D */ "unknown",
- = /* [SC_UNIVERSE]        =3D */ = "universe",
- /* [SC_SPACE]     =       =3D */ "space",
- /* = [SC_FUNCTION]        =3D */ "function",
- = /* [SC_USER]            =3D */ = "user",
- /* [SC_ROLE]      =       =3D */ "role",
- /* = [SC_SEQUENCE]        =3D */ "sequence",
- = /* [SC_COLLATION]       =3D */ = "collation",
+ /* [SC_UKNNOWN] =     =3D */ "unknown",
+ /* = [SC_UNIVERSE] =     =3D */ "universe",
+ /* = [SC_SPACE] =     =3D */ "space",
+ /* = [SC_ENTITY_SPACE]=     =3D */ "space",
+ /* = [SC_FUNCTION] =     =3D */ "function",
+ /* = [SC_ENTITY_FUNCTION]     =3D */ = "function",
+ /* [SC_USER] =     =3D */ "user",
+ /* = [SC_ENTITY_USER] =     =3D */ "user",
+ /* = [SC_ROLE] =     =3D */ "role",
+ /* = [SC_ENTITY_ROLE] =     =3D */ "role",
+ /* = [SC_SEQUENCE] =     =3D */ "sequence",
+ /* = [SC_ENTITY_SEQUENCE]     =3D */ = "sequence",
+ /* [SC_COLLATION]   =   =3D */ "collation",
+ /* = [SC_ENTITY_COLLATION]    =3D */ "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 =3D SC_NEW_OBJECT + 1
+ = * schema_entity_type() relies on this convention.
  */
 enum schema_object_type = {
  SC_UNKNOWN =3D 0,
 = SC_UNIVERSE =3D 1,
  SC_SPACE = =3D 2,
- SC_FUNCTION =3D 3,
- = SC_USER =3D 4,
- SC_ROLE =3D 5,
- = SC_SEQUENCE =3D 6,
- SC_COLLATION =3D 7,
- = schema_object_type_MAX =3D 8
+ = SC_ENTITY_SPACE =3D 3,
+ = SC_FUNCTION =3D 4,
+ SC_ENTITY_FUNCTION =3D 5,
+ = SC_USER =3D 6,
+ SC_ENTITY_USER =3D 7,
+ = SC_ROLE =3D 8,
+ SC_ENTITY_ROLE =3D 9,
+ = SC_SEQUENCE =3D 10,
+ SC_ENTITY_SEQUENCE =3D 11,
+ = SC_COLLATION =3D 12,
+ SC_ENTITY_COLLATION =3D 13,
+ = schema_object_type_MAX =3D 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 =3D universe.access;
 = break;
  }
+ = case SC_ENTITY_SPACE:
+ {
+ = access =3D entity_access.space;
+ = break;
+ }
+ case = SC_ENTITY_FUNCTION:
+ {
+ = access =3D entity_access.function;
+ = break;
+ }
+ case = SC_ENTITY_SEQUENCE:
+ {
+ = access =3D entity_access.sequence;
+ = break;
+ }
  case = SC_SPACE:
  {
- = if (priv->object_id =3D=3D 0) {
- = access =3D entity_access.space;
- = break;
- }
  = struct space *space =3D space_by_id(priv->object_id);
 = if (space)
  = access =3D space->access;
@@ -220,10 +231,6 @@ = access_find(struct priv_def *priv)
  }
 = case SC_FUNCTION:
  {
- = if (priv->object_id =3D=3D 0) {
- = access =3D entity_access.function;
- = break;
- }
  = struct func *func =3D func_by_id(priv->object_id);
 = if (func)
  = access =3D func->access;
@@ -231,10 +238,6 @@ = access_find(struct priv_def *priv)
  }
 = case SC_SEQUENCE:
  {
- = if (priv->object_id =3D=3D 0) {
- = access =3D entity_access.sequence;
- = break;
- }
  = struct sequence *seq =3D sequence_by_id(priv->object_id);
 = if (seq)
  = access =3D 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', '<cluster uuid>']
   - = ['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 =3D 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 =3D 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 =3D 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', '<server_uuid>']
   - = ['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)


= --Apple-Mail=_0EF187BC-EBA2-4A2B-9BE0-7231D22AE127--