[tarantool-patches] [security 1/2] security: Refactor reads from systems spaces

Ilya Markov imarkov at tarantool.org
Wed May 16 15:37:26 MSK 2018


Replace reads from systems spaces with reads from corresponding
system views.

After this patch some error messages are changed:
   * Accessing to objects that are not accessible for current user
   raises the error claiming these objects don't exists.
   * Attempt to add in transaction such methods as object create, drop
   raises an multi-engine transaction error instead of multi-statement
   transaction error.

In scope of #3250
---
 src/box/lua/schema.lua           |  97 +++++++++++++-----------
 src/box/sysview_index.c          |  86 +++++++++++++---------
 test/box/access.result           | 155 ++++++++++++++++++++++++++++++++-------
 test/box/access.test.lua         |  69 +++++++++++------
 test/box/access_bin.result       |  16 ++--
 test/box/access_bin.test.lua     |   4 +-
 test/box/access_misc.result      |  14 ++++
 test/box/access_misc.test.lua    |   9 ++-
 test/box/access_sysview.result   |  11 +--
 test/box/access_sysview.test.lua |   6 +-
 test/box/ddl.result              |   4 +
 test/box/ddl.test.lua            |   2 +
 test/box/on_replace.result       |   8 +-
 test/box/role.result             |   2 +-
 test/box/transaction.result      |  18 ++++-
 test/box/transaction.test.lua    |   4 +
 test/engine/iterator.result      |   2 +-
 test/engine/savepoint.result     |  12 +--
 18 files changed, 368 insertions(+), 151 deletions(-)

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 1a616d5..677325c 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -104,12 +104,12 @@ ffi.cdef[[
 ]]
 
 local function user_or_role_resolve(user)
-    local _user = box.space[box.schema.VUSER_ID]
+    local _vuser = box.space[box.schema.VUSER_ID]
     local tuple
     if type(user) == 'string' then
-        tuple = _user.index.name:get{user}
+        tuple = _vuser.index.name:get{user}
     else
-        tuple = _user:get{user}
+        tuple = _vuser:get{user}
     end
     if tuple == nil then
         return nil
@@ -118,12 +118,12 @@ local function user_or_role_resolve(user)
 end
 
 local function role_resolve(name_or_id)
-    local _user = box.space[box.schema.USER_ID]
+    local _vuser = box.space[box.schema.VUSER_ID]
     local tuple
     if type(name_or_id) == 'string' then
-        tuple = _user.index.name:get{name_or_id}
+        tuple = _vuser.index.name:get{name_or_id}
     elseif type(name_or_id) ~= 'nil' then
-        tuple = _user:get{name_or_id}
+        tuple = _vuser:get{name_or_id}
     end
     if tuple == nil or tuple[4] ~= 'role' then
         return nil
@@ -133,12 +133,12 @@ local function role_resolve(name_or_id)
 end
 
 local function user_resolve(name_or_id)
-    local _user = box.space[box.schema.USER_ID]
+    local _vuser = box.space[box.schema.VUSER_ID]
     local tuple
     if type(name_or_id) == 'string' then
-        tuple = _user.index.name:get{name_or_id}
+        tuple = _vuser.index.name:get{name_or_id}
     elseif type(name_or_id) ~= 'nil' then
-        tuple = _user:get{name_or_id}
+        tuple = _vuser:get{name_or_id}
     end
     if tuple == nil or tuple[4] ~= 'user' then
         return nil
@@ -148,12 +148,12 @@ local function user_resolve(name_or_id)
 end
 
 local function sequence_resolve(name_or_id)
-    local _sequence = box.space[box.schema.SEQUENCE_ID]
+    local _vsequence = box.space[box.schema.VSEQUENCE_ID]
     local tuple
     if type(name_or_id) == 'string' then
-        tuple = _sequence.index.name:get{name_or_id}
+        tuple = _vsequence.index.name:get{name_or_id}
     elseif type(name_or_id) ~= 'nil' then
-        tuple = _sequence:get{name_or_id}
+        tuple = _vsequence:get{name_or_id}
     end
     if tuple ~= nil then
         return tuple[1], tuple
@@ -164,8 +164,9 @@ end
 
 -- Revoke all privileges associated with the given object.
 local function revoke_object_privs(object_type, object_id)
+    local _vpriv = box.space[box.schema.VPRIV_ID]
     local _priv = box.space[box.schema.PRIV_ID]
-    local privs = _priv.index.object:select{object_type, object_id}
+    local privs = _vpriv.index.object:select{object_type, object_id}
     for k, tuple in pairs(privs) do
         local uid = tuple[2]
         _priv:delete{uid, object_type, object_id}
@@ -431,10 +432,15 @@ end
 -- space format - the metadata about space fields
 function box.schema.space.format(id, format)
     local _space = box.space._space
+    local _vspace = box.space._vspace
     check_param(id, 'id', 'number')
 
     if format == nil then
-        return _space:get(id)[7]
+        local tuple = _vspace:get(id)
+        if tuple == nil then
+            box.error(box.error.NO_SUCH_SPACE, '#' .. tostring(id))
+        end
+        return tuple[7]
     else
         check_param(format, 'format', 'table')
         format = update_format(format)
@@ -450,6 +456,7 @@ box.schema.space.drop = function(space_id, space_name, opts)
     check_param_table(opts, { if_exists = 'boolean' })
     local _space = box.space[box.schema.SPACE_ID]
     local _index = box.space[box.schema.INDEX_ID]
+    local _vindex = box.space[box.schema.VINDEX_ID]
     local _truncate = box.space[box.schema.TRUNCATE_ID]
     local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
     local sequence_tuple = _space_sequence:delete{space_id}
@@ -457,7 +464,7 @@ box.schema.space.drop = function(space_id, space_name, opts)
         -- Delete automatically generated sequence.
         box.schema.sequence.drop(sequence_tuple[2])
     end
-    local keys = _index:select(space_id)
+    local keys = _vindex:select(space_id)
     for i = #keys, 1, -1 do
         local v = keys[i]
         _index:delete{v[1], v[2]}
@@ -695,7 +702,8 @@ box.schema.index.create = function(space_id, name, options)
     options = update_param_table(options, options_defaults)
 
     local _index = box.space[box.schema.INDEX_ID]
-    if _index.index.name:get{space_id, name} then
+    local _vindex = box.space[box.schema.VINDEX_ID]
+    if _vindex.index.name:get{space_id, name} then
         if options.if_not_exists then
             return space.index[name], "not created"
         else
@@ -708,7 +716,7 @@ box.schema.index.create = function(space_id, name, options)
         iid = options.id
     else
         -- max
-        local tuple = _index.index[0]
+        local tuple = _vindex.index[0]
             :select(space_id, { limit = 1, iterator = 'LE' })[1]
         if tuple then
             local id = tuple[1]
@@ -1741,12 +1749,12 @@ local function object_resolve(object_type, object_name)
         return space.id
     end
     if object_type == 'function' then
-        local _func = box.space[box.schema.FUNC_ID]
+        local _vfunc = box.space[box.schema.VFUNC_ID]
         local func
         if type(object_name) == 'string' then
-            func = _func.index.name:get{object_name}
+            func = _vfunc.index.name:get{object_name}
         else
-            func = _func:get{object_name}
+            func = _vfunc:get{object_name}
         end
         if func then
             return func[1]
@@ -1762,12 +1770,12 @@ local function object_resolve(object_type, object_name)
         return seq
     end
     if object_type == 'role' then
-        local _user = box.space[box.schema.USER_ID]
+        local _vuser = box.space[box.schema.VUSER_ID]
         local role
         if type(object_name) == 'string' then
-            role = _user.index.name:get{object_name}
+            role = _vuser.index.name:get{object_name}
         else
-            role = _user:get{object_name}
+            role = _vuser:get{object_name}
         end
         if role and role[4] == 'role' then
             return role[1]
@@ -1785,13 +1793,13 @@ local function object_name(object_type, object_id)
     end
     local space
     if object_type == 'space' then
-        space = box.space._space
+        space = box.space._vspace
     elseif object_type == 'sequence' then
         space = box.space._sequence
     elseif object_type == 'function' then
-        space = box.space._func
+        space = box.space._vfunc
     elseif object_type == 'role' or object_type == 'user' then
-        space = box.space._user
+        space = box.space._vuser
     else
         box.error(box.error.UNKNOWN_SCHEMA_OBJECT, object_type)
     end
@@ -1805,7 +1813,8 @@ box.schema.func.create = function(name, opts)
                               if_not_exists = 'boolean',
                               language = 'string'})
     local _func = box.space[box.schema.FUNC_ID]
-    local func = _func.index.name:get{name}
+    local _vfunc = box.space[box.schema.VFUNC_ID]
+    local func = _vfunc.index.name:get{name}
     if func then
         if not opts.if_not_exists then
             box.error(box.error.FUNCTION_EXISTS, name)
@@ -1822,12 +1831,13 @@ box.schema.func.drop = function(name, opts)
     opts = opts or {}
     check_param_table(opts, { if_exists = 'boolean' })
     local _func = box.space[box.schema.FUNC_ID]
+    local _vfunc = box.space[box.schema.VFUNC_ID]
     local fid
     local tuple
     if type(name) == 'string' then
-        tuple = _func.index.name:get{name}
+        tuple = _vfunc.index.name:get{name}
     else
-        tuple = _func:get{name}
+        tuple = _vfunc:get{name}
     end
     if tuple then
         fid = tuple[1]
@@ -1843,12 +1853,12 @@ box.schema.func.drop = function(name, opts)
 end
 
 function box.schema.func.exists(name_or_id)
-    local _func = box.space[box.schema.VFUNC_ID]
+    local _vfunc = box.space[box.schema.VFUNC_ID]
     local tuple = nil
     if type(name_or_id) == 'string' then
-        tuple = _func.index.name:get{name_or_id}
+        tuple = _vfunc.index.name:get{name_or_id}
     elseif type(name_or_id) == 'number' then
-        tuple = _func:get{name_or_id}
+        tuple = _vfunc:get{name_or_id}
     end
     return tuple ~= nil
 end
@@ -2003,8 +2013,9 @@ local function grant(uid, name, privilege, object_type,
         options.grantor = user_or_role_resolve(options.grantor)
     end
     local _priv = box.space[box.schema.PRIV_ID]
+    local _vpriv = box.space[box.schema.VPRIV_ID]
     -- add the granted privilege to the current set
-    local tuple = _priv:get{uid, object_type, oid}
+    local tuple = _vpriv:get{uid, object_type, oid}
     local old_privilege
     if tuple ~= nil then
         old_privilege = tuple[5]
@@ -2039,7 +2050,8 @@ local function revoke(uid, name, privilege, object_type, object_name, options)
     options = options or {}
     local oid = object_resolve(object_type, object_name)
     local _priv = box.space[box.schema.PRIV_ID]
-    local tuple = _priv:get{uid, object_type, oid}
+    local _vpriv = box.space[box.schema.VPRIV_ID]
+    local tuple = _vpriv:get{uid, object_type, oid}
     -- system privileges of admin and guest can't be revoked
     if tuple == nil then
         if options.if_exists then
@@ -2068,32 +2080,32 @@ end
 
 local function drop(uid, opts)
     -- recursive delete of user data
-    local _priv = box.space[box.schema.PRIV_ID]
-    local spaces = box.space[box.schema.SPACE_ID].index.owner:select{uid}
+    local _vpriv = box.space[box.schema.VPRIV_ID]
+    local spaces = box.space[box.schema.VSPACE_ID].index.owner:select{uid}
     for k, tuple in pairs(spaces) do
         box.space[tuple[1]]:drop()
     end
-    local funcs = box.space[box.schema.FUNC_ID].index.owner:select{uid}
+    local funcs = box.space[box.schema.VFUNC_ID].index.owner:select{uid}
     for k, tuple in pairs(funcs) do
         box.schema.func.drop(tuple[1])
     end
     -- if this is a role, revoke this role from whoever it was granted to
-    local grants = _priv.index.object:select{'role', uid}
+    local grants = _vpriv.index.object:select{'role', uid}
     for k, tuple in pairs(grants) do
         revoke(tuple[2], tuple[2], uid)
     end
-    local sequences = box.space[box.schema.SEQUENCE_ID].index.owner:select{uid}
+    local sequences = box.space[box.schema.VSEQUENCE_ID].index.owner:select{uid}
     for k, tuple in pairs(sequences) do
         box.schema.sequence.drop(tuple[1])
     end
     -- xxx: hack, we have to revoke session and usage privileges
     -- of a user using a setuid function in absence of create/drop
     -- privileges and grant option
-    if box.space._user:get{uid}[4] == 'user' then
+    if box.space._vuser:get{uid}[4] == 'user' then
         box.session.su('admin', box.schema.user.revoke, uid,
                        'session,usage', 'universe', nil, {if_exists = true})
     end
-    local privs = _priv.index.primary:select{uid}
+    local privs = _vpriv.index.primary:select{uid}
     for k, tuple in pairs(privs) do
         revoke(uid, uid, tuple[5], tuple[3], tuple[4])
     end
@@ -2150,8 +2162,7 @@ box.schema.user.drop = function(name, opts)
 end
 
 local function info(id)
-    local _priv = box.space._priv
-    local _user = box.space._priv
+    local _priv = box.space._vpriv
     local privs = {}
     for _, v in pairs(_priv:select{id}) do
         table.insert(
diff --git a/src/box/sysview_index.c b/src/box/sysview_index.c
index 8bfc39b..c8e9a1d 100644
--- a/src/box/sysview_index.c
+++ b/src/box/sysview_index.c
@@ -197,23 +197,35 @@ static const struct index_vtab sysview_index_vtab = {
 	/* .end_build = */ generic_index_end_build,
 };
 
+const uint32_t PRIV_WRDA = PRIV_W | PRIV_D | PRIV_A | PRIV_R;
+
+
+/*
+ * System view filters.
+ * Filter must give read access to object, if:
+ * 1. User has modification or read access to universe.
+ * 2. User has read access to according system space.
+ * 3. User has modification or read access to object.
+ * 4. User is an owner of the object.
+ * 5. Other specific conditions of the object of object type.
+ */
 static bool
 vspace_filter(struct space *source, struct tuple *tuple)
 {
 	struct credentials *cr = effective_user();
-	if (PRIV_R & cr->universal_access)
-		return true; /* read access to universe */
-	if (PRIV_R & source->access[cr->auth_token].effective)
-		return true; /* read access to _space space */
-
+	/* If user has global alter, drop privilege she may access all spaces. */
+	if (cr->universal_access & PRIV_WRDA)
+		return true;
+	if (source->access[cr->auth_token].effective & PRIV_R)
+		return true;
 	uint32_t space_id;
 	if (tuple_field_u32(tuple, BOX_SPACE_FIELD_ID, &space_id) != 0)
 		return false;
 	struct space *space = space_cache_find(space_id);
 	if (space == NULL)
 		return false;
-	user_access_t effective = space->access[cr->auth_token].effective;
-	return ((PRIV_R | PRIV_W) & (cr->universal_access | effective) ||
+	uint32_t effective = space->access[cr->auth_token].effective;
+	return (PRIV_WRDA & effective ||
 		space->def->uid == cr->uid);
 }
 
@@ -221,10 +233,11 @@ static bool
 vuser_filter(struct space *source, struct tuple *tuple)
 {
 	struct credentials *cr = effective_user();
-	if (PRIV_R & cr->universal_access)
-		return true; /* read access to universe */
-	if (PRIV_R & source->access[cr->auth_token].effective)
-		return true; /* read access to _user space */
+	/* If user has global alter, drop privilege she may access all users. */
+	if (cr->universal_access & PRIV_WRDA)
+		return true;
+	if (source->access[cr->auth_token].effective & PRIV_R)
+		return true; /* read access to _user space. */
 
 	uint32_t uid;
 	if (tuple_field_u32(tuple, BOX_USER_FIELD_ID, &uid) != 0)
@@ -232,16 +245,19 @@ vuser_filter(struct space *source, struct tuple *tuple)
 	uint32_t owner_id;
 	if (tuple_field_u32(tuple, BOX_USER_FIELD_UID, &owner_id) != 0)
 		return false;
-	return uid == cr->uid || owner_id == cr->uid;
+	return uid == cr->uid || owner_id == cr->uid || uid == PUBLIC;
 }
 
 static bool
 vpriv_filter(struct space *source, struct tuple *tuple)
 {
 	struct credentials *cr = effective_user();
-	if (PRIV_R & cr->universal_access)
-		return true; /* read access to universe */
-	if (PRIV_R & source->access[cr->auth_token].effective)
+	/* If user has global alter, drop privilege
+	 * she may access all privileges
+	 */
+	if (cr->universal_access & PRIV_WRDA)
+		return true;
+	if (source->access[cr->auth_token].effective & PRIV_R)
 		return true; /* read access to _priv space */
 
 	uint32_t grantor_id;
@@ -250,37 +266,43 @@ vpriv_filter(struct space *source, struct tuple *tuple)
 	uint32_t grantee_id;
 	if (tuple_field_u32(tuple, BOX_PRIV_FIELD_UID, &grantee_id) != 0)
 		return false;
-	return grantor_id == cr->uid || grantee_id == cr->uid;
+	const char *type;
+	uint32_t obj_id;
+	if ((type = tuple_field_cstr(tuple, BOX_PRIV_FIELD_OBJECT_TYPE)) == NULL ||
+		tuple_field_u32(tuple, BOX_PRIV_FIELD_OBJECT_ID, &obj_id) != 0)
+		return false;
+	return grantor_id == cr->uid || grantee_id == cr->uid ||
+		(strncmp(type, "role", 4) == 0 && obj_id == PUBLIC);
 }
 
 static bool
 vfunc_filter(struct space *source, struct tuple *tuple)
 {
 	struct credentials *cr = effective_user();
-	if ((PRIV_R | PRIV_X) & cr->universal_access)
-		return true; /* read or execute access to universe */
-	if (PRIV_R & source->access[cr->auth_token].effective)
+	if (cr->universal_access & (PRIV_WRDA | PRIV_X))
+		return true;
+	if (source->access[cr->auth_token].effective & PRIV_R)
 		return true; /* read access to _func space */
 
-	const char *name = tuple_field_cstr(tuple, BOX_FUNC_FIELD_NAME);
+	uint32_t name_len;
+	const char *name = tuple_field_str(tuple, BOX_FUNC_FIELD_NAME,
+					   &name_len);
 	if (name == NULL)
 		return false;
-	uint32_t name_len = strlen(name);
 	struct func *func = func_by_name(name, name_len);
 	assert(func != NULL);
-	user_access_t effective = func->access[cr->auth_token].effective;
-	if (func->def->uid == cr->uid || (PRIV_X & effective))
-		return true;
-	return false;
+	uint32_t effective = func->access[cr->auth_token].effective;
+	return func->def->uid == cr->uid ||
+		((PRIV_WRDA | PRIV_X) & effective);
 }
 
 static bool
 vsequence_filter(struct space *source, struct tuple *tuple)
 {
 	struct credentials *cr = effective_user();
-	if ((PRIV_R | PRIV_X) & cr->universal_access)
-		return true; /* read or execute access to universe */
-	if (PRIV_R & source->access[cr->auth_token].effective)
+	if (cr->universal_access & PRIV_WRDA)
+		return true;
+	if (source->access[cr->auth_token].effective & PRIV_R)
 		return true; /* read access to _sequence space */
 
 	uint32_t id;
@@ -289,13 +311,11 @@ vsequence_filter(struct space *source, struct tuple *tuple)
 	struct sequence *sequence = sequence_by_id(id);
 	if (sequence == NULL)
 		return false;
-	user_access_t effective = sequence->access[cr->auth_token].effective;
-	if (sequence->def->uid == cr->uid || ((PRIV_W | PRIV_R) & effective))
-		return true;
-	return false;
+	uint32_t effective = sequence->access[cr->auth_token].effective;
+	return sequence->def->uid == cr->uid ||
+		(PRIV_WRDA & effective);
 }
 
-
 struct sysview_index *
 sysview_index_new(struct sysview_engine *sysview,
 		  struct index_def *def, const char *space_name)
diff --git a/test/box/access.result b/test/box/access.result
index 5c39bc9..476e594 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -386,7 +386,8 @@ session.su('grantee')
 -- fails - can't suicide - ask the creator to kill you
 box.schema.user.drop('grantee')
 ---
-- error: Read access to space '_user' is denied for user 'grantee'
+- error: 'Failed to drop user or role ''grantee'': the user is active in the current
+    session'
 ...
 session.su('grantor')
 ---
@@ -471,7 +472,7 @@ session.su('user1')
 -- permission denied
 box.schema.user.passwd('admin', 'xxx')
 ---
-- error: Read access to space '_user' is denied for user 'user1'
+- error: User 'admin' is not found
 ...
 session.su('admin')
 ---
@@ -1048,7 +1049,7 @@ session.su("test1")
 ...
 box.schema.user.disable("test")
 ---
-- error: Read access to space '_user' is denied for user 'test1'
+- error: User 'test' is not found
 ...
 session.su("admin")
 ---
@@ -1080,7 +1081,7 @@ session.su("test1")
 ...
 box.schema.user.grant("test", "usage", "universe")
 ---
-- error: Read access to space '_user' is denied for user 'test1'
+- error: User 'test' is not found
 ...
 session.su('admin')
 ---
@@ -1133,6 +1134,9 @@ s = box.schema.space.create("admin_space")
 box.schema.user.grant('guest', 'super')
 ---
 ...
+box.schema.user.grant('guest', 'read', "universe")
+---
+...
 box.session.su('guest')
 ---
 ...
@@ -1173,15 +1177,18 @@ box.schema.space.create('test')
 ...
 box.schema.user.create('test')
 ---
-- error: Read access to space '_user' is denied for user 'guest'
+- error: Write access to space '_user' is denied for user 'guest'
 ...
 box.schema.func.create('test')
 ---
-- error: Read access to space '_func' is denied for user 'guest'
+- error: Write access to space '_func' is denied for user 'guest'
 ...
 box.session.su('admin')
 ---
 ...
+box.schema.user.revoke('guest', 'read', "universe")
+---
+...
 --
 -- gh-2911 on_access_denied trigger
 --
@@ -1357,28 +1364,59 @@ box.schema.user.create("tester")
 s = box.schema.space.create("test")
 ---
 ...
+_ = s:create_index("primary")
+---
+...
+seq = box.schema.sequence.create("test")
+---
+...
 u = box.schema.user.create("test")
 ---
 ...
 f = box.schema.func.create("test")
 ---
 ...
-box.schema.user.grant("tester", "read,execute", "universe")
+-- failed create, auto_increment requires read.
+box.session.su("tester")
+---
+...
+box.schema.space.create("test_space")
+---
+- error: Write access to space '_schema' is denied for user 'tester'
+...
+box.schema.user.create('test_user')
+---
+- error: Read access to space '_user' is denied for user 'tester'
+...
+box.schema.func.create('test_func')
+---
+- error: Read access to space '_func' is denied for user 'tester'
+...
+box.session.su("admin")
+---
+...
+box.schema.user.grant("tester", "read", "universe")
 ---
 ...
 -- failed create
-box.session.su("tester", box.schema.space.create, "test_space")
+box.session.su("tester")
+---
+...
+box.schema.space.create("test_space")
 ---
 - error: Write access to space '_schema' is denied for user 'tester'
 ...
-box.session.su("tester", box.schema.user.create, 'test_user')
+box.schema.user.create('test_user')
 ---
 - error: Write access to space '_user' is denied for user 'tester'
 ...
-box.session.su("tester", box.schema.func.create, 'test_func')
+box.schema.func.create('test_func')
 ---
 - error: Write access to space '_func' is denied for user 'tester'
 ...
+box.session.su("admin")
+---
+...
 --
 -- FIXME 2.0: we still need to grant 'write' on universe
 -- explicitly since we still use process_rw to write to system
@@ -1387,24 +1425,36 @@ box.session.su("tester", box.schema.func.create, 'test_func')
 box.schema.user.grant("tester", "create,write", "universe")
 ---
 ...
+box.session.su("tester")
+---
+...
 -- successful create
-s1 = box.session.su("tester", box.schema.space.create, "test_space")
+s1 = box.schema.space.create("test_space")
 ---
 ...
-_ = box.session.su("tester", box.schema.user.create, 'test_user')
+_ = s1:create_index("primary")
 ---
 ...
-_ = box.session.su("tester", box.schema.func.create, 'test_func')
+_ = box.schema.user.create('test_user')
+---
+...
+_ = box.schema.func.create('test_func')
+---
+...
+seq1 = box.schema.sequence.create('test_seq')
 ---
 ...
 -- successful drop of owned objects
-_ = box.session.su("tester", s1.drop, s1)
+s1:drop()
 ---
 ...
-_ = box.session.su("tester", box.schema.user.drop, 'test_user')
+seq1:drop()
 ---
 ...
-_ = box.session.su("tester", box.schema.func.drop, 'test_func')
+box.schema.user.drop('test_user')
+---
+...
+box.schema.func.drop('test_func')
 ---
 ...
 -- failed alter
@@ -1414,22 +1464,24 @@ _ = box.session.su("tester", box.schema.func.drop, 'test_func')
 -- box.session.su("tester", s.format, s, {name="id", type="unsigned"})
 -- failed drop
 -- box.session.su("tester", s.drop, s)
--- can't use here sudo
--- because drop use sudo inside
--- and currently sudo can't be performed nested
-box.session.su("tester")
+s:drop()
 ---
+- error: Drop access to space 'test' is denied for user 'tester'
+...
+seq:drop()
+---
+- error: Drop access to sequence 'test' is denied for user 'tester'
 ...
 box.schema.user.drop("test")
 ---
 - error: Revoke access to role 'public' is denied for user 'tester'
 ...
-box.session.su("admin")
+box.schema.func.drop("test")
 ---
+- error: Drop access to function 'test' is denied for user 'tester'
 ...
-box.session.su("tester", box.schema.func.drop, "test")
+box.session.su("admin")
 ---
-- error: Drop access to function 'test' is denied for user 'tester'
 ...
 box.schema.user.grant("tester", "drop", "universe")
 ---
@@ -1438,6 +1490,9 @@ box.schema.user.grant("tester", "drop", "universe")
 box.session.su("tester", s.drop, s)
 ---
 ...
+box.session.su("tester", seq.drop, seq)
+---
+...
 box.session.su("tester", box.schema.user.drop, "test")
 ---
 ...
@@ -1510,36 +1565,86 @@ box.schema.func.exists('test')
 ---
 - false
 ...
+box.space._vspace.index.name:get{"test"} ~= nil
+---
+- false
+...
+box.space._vsequence.index.name:get{"test"} ~= nil
+---
+- false
+...
 --
--- create an object, but 'guest' still has no access to it
+-- create an objects, but 'guest' still has no access to them
 --
 box.session.su('admin', box.schema.func.create, 'test')
 ---
 ...
+s = box.session.su('admin', box.schema.space.create, 'test')
+---
+...
+_ = box.session.su('admin', box.schema.sequence.create, 'test')
+---
+...
 box.schema.func.exists('test')
 ---
 - false
 ...
+box.space._vspace.index.name:get{"test"} ~= nil
+---
+- false
+...
+box.space._vsequence.index.name:get{"test"} ~= nil
+---
+- false
+...
 --
 -- grant access, the object should become visible to guest
 --
 box.session.su('admin', box.schema.user.grant, 'guest', 'execute', 'function', 'test')
 ---
 ...
+box.session.su('admin', box.schema.user.grant, 'guest', 'read', 'space', 'test')
+---
+...
+box.session.su('admin', box.schema.user.grant, 'guest', 'read', 'sequence', 'test')
+---
+...
 box.schema.func.exists('test')
 ---
 - true
 ...
+box.space._vspace.index.name:get{"test"} ~= nil
+---
+- true
+...
+box.space._vsequence.index.name:get{"test"} ~= nil
+---
+- true
+...
 --
--- drop object
+-- drop objects
 --
 box.session.su('admin', box.schema.func.drop, 'test')
 ---
 ...
+box.session.su('admin', s.drop, s)
+---
+...
+box.session.su('admin', box.schema.sequence.drop, 'test')
+---
+...
 box.schema.func.exists('test')
 ---
 - false
 ...
+box.space._vspace.index.name:get{"test"} ~= nil
+---
+- false
+...
+box.space._vsequence.index.name:get{"test"} ~= nil
+---
+- false
+...
 --
 -- restore
 --
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 501c766..81f5ed1 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -438,6 +438,7 @@ s:drop()
 --
 s = box.schema.space.create("admin_space")
 box.schema.user.grant('guest', 'super')
+box.schema.user.grant('guest', 'read', "universe")
 box.session.su('guest')
 _ = box.schema.space.create('test')
 box.space.test:drop()
@@ -455,6 +456,7 @@ box.schema.space.create('test')
 box.schema.user.create('test')
 box.schema.func.create('test')
 box.session.su('admin')
+box.schema.user.revoke('guest', 'read', "universe")
 
 --
 -- gh-2911 on_access_denied trigger
@@ -509,14 +511,24 @@ s:drop()
 --
 box.schema.user.create("tester")
 s = box.schema.space.create("test")
+_ = s:create_index("primary")
+seq = box.schema.sequence.create("test")
 u = box.schema.user.create("test")
 f = box.schema.func.create("test")
-box.schema.user.grant("tester", "read,execute", "universe")
 
+-- failed create, auto_increment requires read.
+box.session.su("tester")
+box.schema.space.create("test_space")
+box.schema.user.create('test_user')
+box.schema.func.create('test_func')
+box.session.su("admin")
+box.schema.user.grant("tester", "read", "universe")
 -- failed create
-box.session.su("tester", box.schema.space.create, "test_space")
-box.session.su("tester", box.schema.user.create, 'test_user')
-box.session.su("tester", box.schema.func.create, 'test_func')
+box.session.su("tester")
+box.schema.space.create("test_space")
+box.schema.user.create('test_user')
+box.schema.func.create('test_func')
+box.session.su("admin")
 
 --
 -- FIXME 2.0: we still need to grant 'write' on universe
@@ -524,15 +536,19 @@ box.session.su("tester", box.schema.func.create, 'test_func')
 -- tables from ddl
 --
 box.schema.user.grant("tester", "create,write", "universe")
+box.session.su("tester")
 -- successful create
-s1 = box.session.su("tester", box.schema.space.create, "test_space")
-_ = box.session.su("tester", box.schema.user.create, 'test_user')
-_ = box.session.su("tester", box.schema.func.create, 'test_func')
+s1 = box.schema.space.create("test_space")
+_ = s1:create_index("primary")
+_ = box.schema.user.create('test_user')
+_ = box.schema.func.create('test_func')
+seq1 = box.schema.sequence.create('test_seq')
 
 -- successful drop of owned objects
-_ = box.session.su("tester", s1.drop, s1)
-_ = box.session.su("tester", box.schema.user.drop, 'test_user')
-_ = box.session.su("tester", box.schema.func.drop, 'test_func')
+s1:drop()
+seq1:drop()
+box.schema.user.drop('test_user')
+box.schema.func.drop('test_func')
 
 -- failed alter
 -- box.session.su("tester", s.format, s, {name="id", type="unsigned"})
@@ -543,19 +559,16 @@ _ = box.session.su("tester", box.schema.func.drop, 'test_func')
 
 -- failed drop
 -- box.session.su("tester", s.drop, s)
-
--- can't use here sudo
--- because drop use sudo inside
--- and currently sudo can't be performed nested
-box.session.su("tester")
+s:drop()
+seq:drop()
 box.schema.user.drop("test")
-box.session.su("admin")
-
-box.session.su("tester", box.schema.func.drop, "test")
+box.schema.func.drop("test")
 
+box.session.su("admin")
 box.schema.user.grant("tester", "drop", "universe")
 -- successful drop
 box.session.su("tester", s.drop, s)
+box.session.su("tester", seq.drop, seq)
 box.session.su("tester", box.schema.user.drop, "test")
 box.session.su("tester", box.schema.func.drop, "test")
 
@@ -589,22 +602,36 @@ box.session.su('guest')
 -- has no access to the function
 --
 box.schema.func.exists('test')
+box.space._vspace.index.name:get{"test"} ~= nil
+box.space._vsequence.index.name:get{"test"} ~= nil
 --
--- create an object, but 'guest' still has no access to it
+-- create an objects, but 'guest' still has no access to them
 --
 box.session.su('admin', box.schema.func.create, 'test')
+s = box.session.su('admin', box.schema.space.create, 'test')
+_ = box.session.su('admin', box.schema.sequence.create, 'test')
 box.schema.func.exists('test')
+box.space._vspace.index.name:get{"test"} ~= nil
+box.space._vsequence.index.name:get{"test"} ~= nil
 --
 -- grant access, the object should become visible to guest
 --
 box.session.su('admin', box.schema.user.grant, 'guest', 'execute', 'function', 'test')
+box.session.su('admin', box.schema.user.grant, 'guest', 'read', 'space', 'test')
+box.session.su('admin', box.schema.user.grant, 'guest', 'read', 'sequence', 'test')
 box.schema.func.exists('test')
+box.space._vspace.index.name:get{"test"} ~= nil
+box.space._vsequence.index.name:get{"test"} ~= nil
 --
--- drop object
+-- drop objects
 --
 box.session.su('admin', box.schema.func.drop, 'test')
+box.session.su('admin', s.drop, s)
+box.session.su('admin', box.schema.sequence.drop, 'test')
 box.schema.func.exists('test')
+box.space._vspace.index.name:get{"test"} ~= nil
+box.space._vsequence.index.name:get{"test"} ~= nil
 --
 -- restore
 --
-box.session.su('admin')
+box.session.su('admin')
\ No newline at end of file
diff --git a/test/box/access_bin.result b/test/box/access_bin.result
index b81279c..ce09299 100644
--- a/test/box/access_bin.result
+++ b/test/box/access_bin.result
@@ -50,19 +50,22 @@ box.schema.func.create('setuid_func')
 box.schema.user.grant('guest', 'execute', 'function', 'setuid_func')
 ---
 ...
+box.schema.user.grant('guest', 'read', 'universe')
+---
+...
 c = remote.connect(box.cfg.listen)
 ---
 ...
 c:call("setuid_func")
 ---
-- error: Read access to space 'setuid_space' is denied for user 'guest'
+- error: Write access to space 'setuid_space' is denied for user 'guest'
 ...
 session.su('guest')
 ---
 ...
 setuid_func()
 ---
-- error: Read access to space 'setuid_space' is denied for user 'guest'
+- error: Write access to space 'setuid_space' is denied for user 'guest'
 ...
 session.su('admin')
 ---
@@ -85,7 +88,7 @@ session.su('guest')
 ...
 setuid_func()
 ---
-- error: Read access to space 'setuid_space' is denied for user 'guest'
+- error: Write access to space 'setuid_space' is denied for user 'guest'
 ...
 session.su('admin')
 ---
@@ -122,7 +125,7 @@ session.su('guest')
 ...
 setuid_func()
 ---
-- error: Read access to space 'setuid_space' is denied for user 'guest'
+- error: Write access to space 'setuid_space' is denied for user 'guest'
 ...
 session.su('admin')
 ---
@@ -296,6 +299,9 @@ test:drop()
 box.schema.user.grant('guest', 'execute', 'universe')
 ---
 ...
+box.schema.user.revoke('guest', 'read', 'universe')
+---
+...
 function f1() return box.space._func:get(1)[4] end
 ---
 ...
@@ -344,7 +350,7 @@ box.session.su('admin', box.session.user())
 ---
 - error: 'bad argument #2 to ''?'' (function expected, got string)'
 ...
--- clenaup
+-- cleanup
 box.session.su('admin')
 ---
 ...
diff --git a/test/box/access_bin.test.lua b/test/box/access_bin.test.lua
index cb3a50c..b4f5f64 100644
--- a/test/box/access_bin.test.lua
+++ b/test/box/access_bin.test.lua
@@ -20,6 +20,7 @@ index = setuid_space:create_index('primary')
 setuid_func = function() return box.space.setuid_space:auto_increment{} end
 box.schema.func.create('setuid_func')
 box.schema.user.grant('guest', 'execute', 'function', 'setuid_func')
+box.schema.user.grant('guest', 'read', 'universe')
 c = remote.connect(box.cfg.listen)
 c:call("setuid_func")
 session.su('guest')
@@ -112,6 +113,7 @@ test:drop()
 --
 -- notice that guest can execute stuff, but can't read space _func
 box.schema.user.grant('guest', 'execute', 'universe')
+box.schema.user.revoke('guest', 'read', 'universe')
 function f1() return box.space._func:get(1)[4] end
 function f2() return box.space._func:get(2)[4] end
 box.schema.func.create('f1')
@@ -131,5 +133,5 @@ box.schema.func.drop('f2')
 --
 box.session.su('admin', box.session.user)
 box.session.su('admin', box.session.user())
--- clenaup
+-- cleanup
 box.session.su('admin')
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 3a56a4c..8bf99f2 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -175,6 +175,17 @@ gs = box.schema.space.create('guest_space')
 ---
 - error: Write access to space '_schema' is denied for user 'guest'
 ...
+--
+-- FIXME: object create calls system space auto_increment, which requires
+-- read and write privileges. Create privilege must solve this.
+--
+box.schema.func.create('guest_func')
+---
+- error: Read access to space '_func' is denied for user 'guest'
+...
+session.su('admin', box.schema.user.grant, "guest", "read", "universe")
+---
+...
 box.schema.func.create('guest_func')
 ---
 - error: Read access to space '_func' is denied for user 'guest'
@@ -182,6 +193,9 @@ box.schema.func.create('guest_func')
 session.su('admin')
 ---
 ...
+box.schema.user.revoke("guest", "read", "universe")
+---
+...
 s:select()
 ---
 - - [2]
diff --git a/test/box/access_misc.test.lua b/test/box/access_misc.test.lua
index cf6447e..27064c4 100644
--- a/test/box/access_misc.test.lua
+++ b/test/box/access_misc.test.lua
@@ -70,9 +70,16 @@ s:insert({4})
 s:delete({3})
 s:drop()
 gs = box.schema.space.create('guest_space')
+--
+-- FIXME: object create calls system space auto_increment, which requires
+-- read and write privileges. Create privilege must solve this.
+--
+box.schema.func.create('guest_func')
+session.su('admin', box.schema.user.grant, "guest", "read", "universe")
 box.schema.func.create('guest_func')
-
 session.su('admin')
+box.schema.user.revoke("guest", "read", "universe")
+
 s:select()
 --
 -- Create user with universe read&write grants
diff --git a/test/box/access_sysview.result b/test/box/access_sysview.result
index 340ed21..20efd2b 100644
--- a/test/box/access_sysview.result
+++ b/test/box/access_sysview.result
@@ -266,11 +266,11 @@ box.session.su('guest')
 ...
 #box.space._vuser:select{}
 ---
-- 1
+- 5
 ...
 #box.space._vpriv:select{}
 ---
-- 2
+- 15
 ...
 #box.space._vfunc:select{}
 ---
@@ -343,7 +343,7 @@ box.session.su('guest')
 -- _vuser
 --
 -- a guest user can read information about itself
-t = box.space._vuser:select(); return #t == 1 and t[1][3] == 'guest'
+t = box.space._vuser:select(); for i = 1, #t do if t[i][3] == 'guest' then return true end end return false
 ---
 - true
 ...
@@ -526,8 +526,9 @@ box.schema.user.grant('guest', 'execute', 'function', 'test')
 box.session.su('guest')
 ---
 ...
-#box.space._vfunc:select{} = cnt + 1
+#box.space._vfunc:select{} == func_cnt
 ---
+- true
 ...
 box.session.su('admin')
 ---
@@ -551,7 +552,7 @@ box.schema.user.grant('guest', 'execute', 'universe')
 box.session.su('guest')
 ---
 ...
-#box.space._vfunc:select{} == cnt + 1
+#box.space._vfunc:select{} == func_cnt
 ---
 - true
 ...
diff --git a/test/box/access_sysview.test.lua b/test/box/access_sysview.test.lua
index 7955ffc..4cc5611 100644
--- a/test/box/access_sysview.test.lua
+++ b/test/box/access_sysview.test.lua
@@ -134,7 +134,7 @@ box.session.su('guest')
 --
 
 -- a guest user can read information about itself
-t = box.space._vuser:select(); return #t == 1 and t[1][3] == 'guest'
+t = box.space._vuser:select(); for i = 1, #t do if t[i][3] == 'guest' then return true end end return false
 
 -- read access to original space also allow to read a view
 box.session.su('admin')
@@ -218,7 +218,7 @@ box.session.su('admin')
 box.schema.user.grant('guest', 'execute', 'function', 'test')
 box.session.su('guest')
 
-#box.space._vfunc:select{} = cnt + 1
+#box.space._vfunc:select{} == func_cnt
 
 box.session.su('admin')
 box.schema.user.revoke('guest', 'execute', 'function', 'test')
@@ -230,7 +230,7 @@ box.session.su('admin')
 box.schema.user.grant('guest', 'execute', 'universe')
 box.session.su('guest')
 
-#box.space._vfunc:select{} == cnt + 1
+#box.space._vfunc:select{} == func_cnt
 
 box.session.su('admin')
 box.schema.user.revoke('guest', 'execute', 'universe')
diff --git a/test/box/ddl.result b/test/box/ddl.result
index f249f8f..15faa9a 100644
--- a/test/box/ddl.result
+++ b/test/box/ddl.result
@@ -565,6 +565,10 @@ test_run:cmd("setopt delimiter ''");
 ---
 - true
 ...
+-- finish transaction
+box.rollback()
+---
+...
 _ = c:get()
 ---
 ...
diff --git a/test/box/ddl.test.lua b/test/box/ddl.test.lua
index 6029c6e..93501bd 100644
--- a/test/box/ddl.test.lua
+++ b/test/box/ddl.test.lua
@@ -226,6 +226,8 @@ test_latch:create_index("sec2", {unique = true, parts = {2, 'unsigned'}})
 box.commit();
 
 test_run:cmd("setopt delimiter ''");
+-- finish transaction
+box.rollback()
 
 _ = c:get()
 test_latch:drop() -- this is where everything stops
diff --git a/test/box/on_replace.result b/test/box/on_replace.result
index a961df1..8c52e12 100644
--- a/test/box/on_replace.result
+++ b/test/box/on_replace.result
@@ -471,7 +471,7 @@ t = s:on_replace(function () s:create_index('sec') end, t)
 ...
 s:replace({2, 3})
 ---
-- error: Space _index does not support multi-statement transactions
+- error: A multi-statement transaction can not use multiple storage engines
 ...
 t = s:on_replace(function () box.schema.user.create('newu') end, t)
 ---
@@ -506,21 +506,21 @@ t = s:on_replace(function () s:drop() end, t)
 ...
 s:replace({5, 6})
 ---
-- error: DDL does not support multi-statement transactions
+- error: A multi-statement transaction can not use multiple storage engines
 ...
 t = s:on_replace(function () box.schema.func.create('newf') end, t)
 ---
 ...
 s:replace({6, 7})
 ---
-- error: Space _func does not support multi-statement transactions
+- error: A multi-statement transaction can not use multiple storage engines
 ...
 t = s:on_replace(function () box.schema.user.grant('guest', 'read,write', 'space', 'test_on_repl_ddl') end, t)
 ---
 ...
 s:replace({7, 8})
 ---
-- error: Space _priv does not support multi-statement transactions
+- error: A multi-statement transaction can not use multiple storage engines
 ...
 t = s:on_replace(function () s:rename('newname') end, t)
 ---
diff --git a/test/box/role.result b/test/box/role.result
index 736ec85..806cea9 100644
--- a/test/box/role.result
+++ b/test/box/role.result
@@ -662,7 +662,7 @@ box.session.su('john')
 -- error
 box.schema.user.grant('grantee', 'role')
 ---
-- error: Read access to space '_user' is denied for user 'john'
+- error: User 'grantee' is not found
 ...
 --
 box.session.su('admin')
diff --git a/test/box/transaction.result b/test/box/transaction.result
index 0580331..2fb1abf 100644
--- a/test/box/transaction.result
+++ b/test/box/transaction.result
@@ -69,7 +69,7 @@ box.rollback();
 ...
 box.begin() box.schema.func.create('test');
 ---
-- error: Space _func does not support multi-statement transactions
+- error: A multi-statement transaction can not use multiple storage engines
 ...
 box.rollback();
 ---
@@ -83,7 +83,7 @@ box.rollback();
 ...
 box.begin() box.schema.user.grant('guest', 'read', 'space', '_priv');
 ---
-- error: Space _priv does not support multi-statement transactions
+- error: A multi-statement transaction can not use multiple storage engines
 ...
 box.rollback();
 ---
@@ -95,6 +95,20 @@ box.begin() box.space._user:delete{box.schema.GUEST_ID};
 box.rollback();
 ---
 ...
+box.begin() box.space._space:delete{box.schema.CLUSTER_ID};
+---
+- error: DDL does not support multi-statement transactions
+...
+box.rollback();
+---
+...
+box.begin() box.space._sequence:insert{1, 1, 'test', 1, 1, 2, 1, 0, false};
+---
+- error: Space _sequence does not support multi-statement transactions
+...
+box.rollback();
+---
+...
 box.begin() box.space._schema:insert{'test'};
 ---
 - error: Space _schema does not support multi-statement transactions
diff --git a/test/box/transaction.test.lua b/test/box/transaction.test.lua
index ca6de44..14d1a69 100644
--- a/test/box/transaction.test.lua
+++ b/test/box/transaction.test.lua
@@ -38,6 +38,10 @@ box.begin() box.schema.user.grant('guest', 'read', 'space', '_priv');
 box.rollback();
 box.begin() box.space._user:delete{box.schema.GUEST_ID};
 box.rollback();
+box.begin() box.space._space:delete{box.schema.CLUSTER_ID};
+box.rollback();
+box.begin() box.space._sequence:insert{1, 1, 'test', 1, 1, 2, 1, 0, false};
+box.rollback();
 box.begin() box.space._schema:insert{'test'};
 box.rollback();
 box.begin() box.space._cluster:insert{123456789, 'abc'};
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index 423ed0b..ae14c43 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4211,7 +4211,7 @@ s:replace{35}
 ...
 state, value = gen(param,state)
 ---
-- error: 'builtin/box/schema.lua:985: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:993: usage: next(param, state)'
 ...
 value
 ---
diff --git a/test/engine/savepoint.result b/test/engine/savepoint.result
index d440efa..dc2ad79 100644
--- a/test/engine/savepoint.result
+++ b/test/engine/savepoint.result
@@ -14,7 +14,7 @@ s1 = box.savepoint()
 ...
 box.rollback_to_savepoint(s1)
 ---
-- error: 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
+- error: 'builtin/box/schema.lua:301: Usage: box.rollback_to_savepoint(savepoint)'
 ...
 box.begin() s1 = box.savepoint()
 ---
@@ -323,27 +323,27 @@ test_run:cmd("setopt delimiter ''");
 ok1, errmsg1
 ---
 - false
-- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:301: Usage: box.rollback_to_savepoint(savepoint)'
 ...
 ok2, errmsg2
 ---
 - false
-- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:301: Usage: box.rollback_to_savepoint(savepoint)'
 ...
 ok3, errmsg3
 ---
 - false
-- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:301: Usage: box.rollback_to_savepoint(savepoint)'
 ...
 ok4, errmsg4
 ---
 - false
-- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:301: Usage: box.rollback_to_savepoint(savepoint)'
 ...
 ok5, errmsg5
 ---
 - false
-- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:301: Usage: box.rollback_to_savepoint(savepoint)'
 ...
 s:select{}
 ---
-- 
2.7.4







More information about the Tarantool-patches mailing list