From d5936b25554f434d9e36e982db32ddbfac6a67a4 Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Thu, 18 Dec 2014 00:11:16 +0300
Subject: [PATCH] Implement error checks in grant/revoke.

---
 src/box/alter.cc                 |  3 ++-
 src/box/lua/schema.lua           | 27 ++++++++++++++++++++-------
 src/errcode.h                    |  4 ++++
 test/box/access.result           |  1 +
 test/box/access_misc.result      |  3 +++
 test/box/box.net.box.result      |  3 +++
 test/box/box.net.box.test.lua    |  1 +
 test/box/misc.result             |  6 +++++-
 test/box/protocol.result         |  3 +++
 test/box/protocol.test.lua       |  1 +
 test/box/role.result             | 24 ++++++++++++++++++++++++
 test/box/role.test.lua           | 14 ++++++++++++++
 test/box/session.result          |  3 +++
 test/box/session.test.lua        |  1 +
 test/replication/cluster.result  |  3 +++
 test/replication/cluster.test.py |  1 +
 16 files changed, 89 insertions(+), 9 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 2808315656..c2b1b97182 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -1502,7 +1502,8 @@ priv_def_check(struct priv_def *priv)
 				  role ? role->name :
 				  int2str(priv->object_id));
 		}
-		/* Only the creator of the role can grant or revoke it.
+		/*
+		 * Only the creator of the role can grant or revoke it.
 		 * Everyone can grant 'PUBLIC' role.
 		 */
 		if (role->owner != grantor->uid && grantor->uid != ADMIN &&
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index cd2303342d..6df1b9b413 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1044,7 +1044,7 @@ box.schema.user.grant = function(user_name, privilege, object_type,
     if uid == nil then
         box.error(box.error.NO_SUCH_USER, user_name)
     end
-    privilege = privilege_resolve(privilege)
+    privilege_hex = privilege_resolve(privilege)
     local oid = object_resolve(object_type, object_name)
     if grantor == nil then
         grantor = session.uid()
@@ -1060,11 +1060,19 @@ box.schema.user.grant = function(user_name, privilege, object_type,
     else
         old_privilege = 0
     end
-    privilege = bit.bor(privilege, old_privilege)
+    privilege_hex = bit.bor(privilege_hex, old_privilege)
     -- do not execute a replace if it does not change anything
-    -- XXX bug: new grantor replaces the old one, old grantor is lost
-    if privilege ~= old_privilege then
-        _priv:replace{grantor, uid, object_type, oid, privilege}
+    -- XXX bug if we decide to add a grant option: new grantor
+    -- replaces the old one, old grantor is lost
+    if privilege_hex ~= old_privilege then
+        _priv:replace{grantor, uid, object_type, oid, privilege_hex}
+    else
+        if object_type == 'role' then
+            box.error(box.error.ROLE_GRANTED, user_name, object_name)
+        else
+            box.error(box.error.PRIV_GRANTED, user_name, privilege,
+                      object_type, object_name)
+        end
     end
 end
 
@@ -1082,13 +1090,18 @@ box.schema.user.revoke = function(user_name, privilege, object_type, object_name
     if uid == nil then
         box.error(box.error.NO_SUCH_USER, name)
     end
-    privilege = privilege_resolve(privilege)
     local oid = object_resolve(object_type, object_name)
     local _priv = box.space[box.schema.PRIV_ID]
     local tuple = _priv:get{uid, object_type, oid}
     if tuple == nil then
-        return
+        if object_type == 'role' then
+            box.error(box.error.ROLE_NOT_GRANTED, user_name, object_name)
+        else
+            box.error(box.error.PRIV_NOT_GRANTED, user_name, privilege,
+                      object_type, object_name)
+        end
     end
+    privilege = privilege_resolve(privilege)
     local old_privilege = tuple[5]
     local grantor = tuple[1]
     -- sic:
diff --git a/src/errcode.h b/src/errcode.h
index 5160e86988..a128fe3319 100644
--- a/src/errcode.h
+++ b/src/errcode.h
@@ -138,6 +138,10 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/* 86 */_(ER_TUPLE_REF_OVERFLOW,	1, "Tuple reference counter overflow") \
 	/* 87 */_(ER_ROLE_LOOP,			2, "Granting role '%s' to role '%s' would create a loop") \
 	/* 88 */_(ER_GRANT,			2, "Incorrect grant arguments: %s") \
+	/* 89 */_(ER_PRIV_GRANTED,		2, "User '%s' already has %s access on %s '%s'") \
+	/* 90 */_(ER_ROLE_GRANTED,		2, "User '%s' already has role '%s'") \
+	/* 91 */_(ER_PRIV_NOT_GRANTED,		2, "User '%s' does not have %s access on %s '%s'") \
+	/* 92 */_(ER_ROLE_NOT_GRANTED,		2, "User '%s' does not have role '%s'") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/test/box/access.result b/test/box/access.result
index 975ddea6cc..2e5bf7f937 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -360,6 +360,7 @@ box.space._priv:select{id}
 ...
 box.schema.user.grant('user', 'read', 'universe')
 ---
+- error: User 'user' already has read access on universe 'nil'
 ...
 box.space._priv:select{id}
 ---
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 27e7d7afb7..b181cbba87 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -53,6 +53,7 @@ box.schema.user.grant('testus', 'read', 'space', 'admin_space')
 ...
 box.schema.user.grant('testus', 'read', 'space', 'admin_space')
 ---
+- error: User 'testus' already has read access on space 'admin_space'
 ...
 session.su('testus')
 ---
@@ -84,6 +85,7 @@ box.schema.user.revoke('testus', 'read', 'space', 'admin_space')
 ...
 box.schema.user.revoke('testus', 'read', 'space', 'admin_space')
 ---
+- error: User 'testus' does not have read access on space 'admin_space'
 ...
 session.su('testus')
 ---
@@ -402,6 +404,7 @@ session.su('admin')
 ...
 box.schema.user.revoke('testuser', 'read, write, execute', 'universe')
 ---
+- error: User 'testuser' does not have read, write, execute access on universe 'nil'
 ...
 --
 -- Check that itertors check privileges
diff --git a/test/box/box.net.box.result b/test/box/box.net.box.result
index 19202db6c0..448a5644ed 100644
--- a/test/box/box.net.box.result
+++ b/test/box/box.net.box.result
@@ -758,3 +758,6 @@ file_log:close()
 ---
 - true
 ...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
diff --git a/test/box/box.net.box.test.lua b/test/box/box.net.box.test.lua
index a244dd421a..2839f9eb83 100644
--- a/test/box/box.net.box.test.lua
+++ b/test/box/box.net.box.test.lua
@@ -311,3 +311,4 @@ end;
 --# setopt delimiter ''
 
 file_log:close()
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
diff --git a/test/box/misc.result b/test/box/misc.result
index e08f06bee6..4a6b2751e5 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -207,8 +207,12 @@ t;
   - 'box.error.USER_EXISTS : 46'
   - 'box.error.WAL_IO : 40'
   - 'box.error.CREATE_USER : 43'
+  - 'box.error.DROP_PRIMARY_KEY : 17'
+  - 'box.error.PRIV_GRANTED : 89'
   - 'box.error.CREATE_SPACE : 9'
+  - 'box.error.PRIV_NOT_GRANTED : 91'
   - 'box.error.GRANT : 88'
+  - 'box.error.ROLE_GRANTED : 90'
   - 'box.error.TUPLE_REF_OVERFLOW : 86'
   - 'box.error.UNKNOWN_SCHEMA_OBJECT : 49'
   - 'box.error.PROC_LUA : 32'
@@ -258,7 +262,7 @@ t;
   - 'box.error.ARG_TYPE : 26'
   - 'box.error.INDEX_FIELD_COUNT : 39'
   - 'box.error.READONLY : 7'
-  - 'box.error.DROP_PRIMARY_KEY : 17'
+  - 'box.error.ROLE_NOT_GRANTED : 92'
   - 'box.error.DROP_SPACE : 11'
   - 'box.error.UNKNOWN_REQUEST_TYPE : 48'
   - 'box.error.INVALID_XLOG_ORDER : 76'
diff --git a/test/box/protocol.result b/test/box/protocol.result
index 86698ffcea..0783bf6302 100644
--- a/test/box/protocol.result
+++ b/test/box/protocol.result
@@ -51,3 +51,6 @@ conn:close()
 space:drop()
 ---
 ...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
diff --git a/test/box/protocol.test.lua b/test/box/protocol.test.lua
index 1349541bbc..307909c843 100644
--- a/test/box/protocol.test.lua
+++ b/test/box/protocol.test.lua
@@ -18,3 +18,4 @@ conn.space[space.id]:select(3, { iterator = 'LT' })
 conn:close()
 
 space:drop()
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
diff --git a/test/box/role.result b/test/box/role.result
index df3d02c808..310edbaabf 100644
--- a/test/box/role.result
+++ b/test/box/role.result
@@ -67,9 +67,11 @@ box.schema.user.grant('tester', 'execute', 'role', 'tester')
 -- test granting a non-execute grant on a role - error
 box.schema.user.grant('tester', 'write', 'role', 'iddqd')
 ---
+- error: User 'tester' already has role 'iddqd'
 ...
 box.schema.user.grant('tester', 'read', 'role', 'iddqd')
 ---
+- error: User 'tester' already has role 'iddqd'
 ...
 -- test granting role to a role
 box.schema.user.grant('iddqd', 'execute', 'role', 'iddqd')
@@ -82,9 +84,11 @@ box.schema.user.grant('iddqd', 'iddqd')
 ...
 box.schema.user.revoke('iddqd', 'iddqd')
 ---
+- error: User 'iddqd' does not have role 'iddqd'
 ...
 box.schema.user.grant('tester', 'iddqd')
 ---
+- error: User 'tester' already has role 'iddqd'
 ...
 box.schema.user.revoke('tester', 'iddqd')
 ---
@@ -631,10 +635,12 @@ box.schema.user.create('john')
 box.session.su('john')
 ---
 ...
+-- error
 box.schema.role.grant('grantee', 'role')
 ---
 - error: Read access denied for user 'john' to space '_user'
 ...
+--
 box.session.su('admin')
 ---
 ...
@@ -655,6 +661,24 @@ box.schema.role.grant('grantee', 'read', 'space', 'test')
 ---
 - error: Read access denied for user 'john'
 ...
+--
+-- granting 'public' is however an exception - everyone
+-- can grant 'public' role, it's implicitly granted with 
+-- a grant option.
+-- 
+box.schema.role.grant('grantee', 'public')
+---
+- error: User 'grantee' already has role 'public'
+...
+-- 
+-- revoking role 'public' is another deal - only the 
+-- superuser can do that, and even that would be useless,
+-- since one can still re-grant it back to oneself.
+-- 
+box.schema.role.revoke('grantee', 'public')
+---
+- error: Create or drop access denied for user 'john'
+...
 box.session.su('admin')
 ---
 ...
diff --git a/test/box/role.test.lua b/test/box/role.test.lua
index 3af64a559f..14acba2421 100644
--- a/test/box/role.test.lua
+++ b/test/box/role.test.lua
@@ -244,13 +244,27 @@ box.schema.role.grant('grantee', 'role')
 box.schema.role.revoke('grantee', 'role')
 box.schema.user.create('john')
 box.session.su('john')
+-- error
 box.schema.role.grant('grantee', 'role')
+--
 box.session.su('admin')
 _ = box.schema.space.create('test')
 box.schema.user.grant('john', 'read,write,execute', 'universe')
 box.session.su('john')
 box.schema.role.grant('grantee', 'role')
 box.schema.role.grant('grantee', 'read', 'space', 'test')
+--
+-- granting 'public' is however an exception - everyone
+-- can grant 'public' role, it's implicitly granted with 
+-- a grant option.
+-- 
+box.schema.role.grant('grantee', 'public')
+-- 
+-- revoking role 'public' is another deal - only the 
+-- superuser can do that, and even that would be useless,
+-- since one can still re-grant it back to oneself.
+-- 
+box.schema.role.revoke('grantee', 'public')
 
 box.session.su('admin')
 box.schema.user.drop('john')
diff --git a/test/box/session.result b/test/box/session.result
index a087de691b..49e15c2196 100644
--- a/test/box/session.result
+++ b/test/box/session.result
@@ -224,3 +224,6 @@ fiber = nil
 session = nil
 ---
 ...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
diff --git a/test/box/session.test.lua b/test/box/session.test.lua
index da1bc63e8d..367755ebd1 100644
--- a/test/box/session.test.lua
+++ b/test/box/session.test.lua
@@ -88,3 +88,4 @@ session.uid()
 session.user()
 fiber = nil
 session = nil
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
diff --git a/test/replication/cluster.result b/test/replication/cluster.result
index 8be88a7195..fc11db893f 100644
--- a/test/replication/cluster.result
+++ b/test/replication/cluster.result
@@ -131,3 +131,6 @@ box.info.vclock[11]
 ---
 - 0
 ...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
diff --git a/test/replication/cluster.test.py b/test/replication/cluster.test.py
index aa41dc0a30..5beec7a8e8 100644
--- a/test/replication/cluster.test.py
+++ b/test/replication/cluster.test.py
@@ -98,3 +98,4 @@ replica.admin('box.info.vclock[%d]' % replica_id3)
 # Cleanup
 sys.stdout.pop_filter()
 
+master.admin("box.schema.user.revoke('guest', 'read,write,execute', 'universe')")
-- 
GitLab