diff --git a/src/luamod.lua b/src/luamod.lua index 167472c963780e3c542982438eb04710daccb011..847c8b7fe5dffbce634110cd7ee567aa4c453da5 100644 --- a/src/luamod.lua +++ b/src/luamod.lua @@ -97,8 +97,7 @@ Params: 2. password (string) 3. opts (table) - if_not_exists (boolean), if true, do nothing if user with given name already exists - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -112,6 +111,9 @@ function pico.create_user(user, password, opts) box.internal.check_param(password, 'password', 'string') box.internal.check_param_table(opts, { if_not_exists = 'boolean', timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -145,7 +147,7 @@ function pico.create_user(user, password, opts) } } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end help.change_password = [[ @@ -166,8 +168,7 @@ Params: 1. user (string), username 2. password (string) 3. opts (table) - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -181,6 +182,9 @@ function pico.change_password(user, password, opts) box.internal.check_param(password, 'password', 'string') box.internal.check_param_table(opts, { timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -205,7 +209,7 @@ function pico.change_password(user, password, opts) } } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end help.drop_user = [[ @@ -227,8 +231,7 @@ Params: 1. user (string), username 2. opts (table) - if_exists (boolean), if true do nothing if user with given name doesn't exist - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -241,6 +244,9 @@ function pico.drop_user(user, opts) box.internal.check_param(user, 'user', 'string') box.internal.check_param_table(opts, { if_exists = 'boolean', timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -263,7 +269,7 @@ function pico.drop_user(user, opts) schema_version = next_schema_version(), } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end help.create_role = [[ @@ -281,8 +287,7 @@ Params: 1. name (string), role name 2. opts (table) - if_not_exists (boolean), if true, do nothing if role with given name already exists - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -295,6 +300,9 @@ function pico.create_role(role, opts) box.internal.check_param(role, 'role', 'string') box.internal.check_param_table(opts, { if_not_exists = 'boolean', timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -322,7 +330,7 @@ function pico.create_role(role, opts) } } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end help.drop_role = [[ @@ -340,8 +348,7 @@ Params: 1. role (string), role name 2. opts (table) - if_exists (boolean), if true do nothing if role with given name doesn't exist - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -354,6 +361,9 @@ function pico.drop_role(role, opts) box.internal.check_param(role, 'role', 'string') box.internal.check_param_table(opts, { if_exists = 'boolean', timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -376,7 +386,7 @@ function pico.drop_role(role, opts) schema_version = next_schema_version(), } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end -- A lookup map @@ -571,8 +581,7 @@ Params: entire class of entities, see examples below. 5. opts (table) - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -606,6 +615,9 @@ function pico.grant_privilege(grantee, privilege, object_type, object_name, opts box.internal.check_param(object_name, 'object_name', 'string') box.internal.check_param_table(opts, { timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -645,7 +657,7 @@ function pico.grant_privilege(grantee, privilege, object_type, object_name, opts }, } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end help.revoke_privilege = [[ @@ -678,8 +690,7 @@ Params: entire class of entities, see pico.help("pico.grant_privilege") for details. 5. opts (table) - - timeout (number), wait for this many seconds for the proposed entry - to be applied locally, default: 3 seconds + - timeout (number), seconds Returns: @@ -696,6 +707,9 @@ function pico.revoke_privilege(grantee, privilege, object_type, object_name, opt box.internal.check_param(object_name, 'object_name', 'string') box.internal.check_param_table(opts, { timeout = 'number' }) opts = opts or {} + if not opts.timeout then + box.error(box.error.ILLEGAL_PARAMS, 'opts.timeout is mandatory') + end end) if not ok then return nil, err @@ -735,7 +749,7 @@ function pico.revoke_privilege(grantee, privilege, object_type, object_name, opt }, } - return pico._prepare_schema_change(op, opts.timeout or 3) + return pico._prepare_schema_change(op, opts.timeout) end help.drop_space = [[ diff --git a/test/int/test_acl.py b/test/int/test_acl.py index d1c72d25294984d793c03c766d4cf6bf5d2ae788..4ebba6752fa1a99cd1041b41660496ead491364d 100644 --- a/test/int/test_acl.py +++ b/test/int/test_acl.py @@ -19,15 +19,15 @@ def test_acl_lua_api(cluster: Cluster): i1.call("pico.create_user", "Dave") # This is probably not ok. - i1.call("pico.create_user", "Dave", "") + i1.call("pico.create_user", "Dave", "", dict(timeout=3)) # Already exists -> error. with pytest.raises(ReturnError, match="User 'Dave' already exists"): - i1.call("pico.create_user", "Dave", "") + i1.call("pico.create_user", "Dave", "", dict(timeout=3)) # Role already exists -> error. with pytest.raises(ReturnError, match="Role 'super' already exists"): - i1.call("pico.create_user", "super", "") + i1.call("pico.create_user", "super", "", dict(timeout=3)) # # pico.create_role @@ -38,15 +38,15 @@ def test_acl_lua_api(cluster: Cluster): i1.call("pico.create_role") # Ok. - i1.call("pico.create_role", "Parent") + i1.call("pico.create_role", "Parent", dict(timeout=3)) # Already exists -> error. with pytest.raises(ReturnError, match="Role 'Parent' already exists"): - i1.call("pico.create_role", "Parent") + i1.call("pico.create_role", "Parent", dict(timeout=3)) # User already exists -> error. with pytest.raises(ReturnError, match="User 'Dave' already exists"): - i1.call("pico.create_role", "Dave") + i1.call("pico.create_role", "Dave", dict(timeout=3)) # # pico.grant_privilege / pico.revoke_privilege parameter verification @@ -57,27 +57,27 @@ def test_acl_lua_api(cluster: Cluster): with pytest.raises(ReturnError, match="grantee should be a string"): i1.call(f"pico.{f}") - # No such user -> error. - with pytest.raises(ReturnError, match="User 'User is not found' is not found"): - i1.call(f"pico.{f}", "User is not found", "execute", "universe") - # No privilege -> error. with pytest.raises(ReturnError, match="privilege should be a string"): i1.call(f"pico.{f}", "Dave") + # No such user -> error. + with pytest.raises(ReturnError, match="User 'User is not found' is not found"): + i1.call(f"pico.{f}", "User is not found", "execute", "universe", None, dict(timeout=3)) + # No such privilege -> error. with pytest.raises( ReturnError, match=rf"unsupported privilege 'boogie', see pico.help\('{f}'\) for details", ): - i1.call(f"pico.{f}", "Dave", "boogie", "universe") + i1.call(f"pico.{f}", "Dave", "boogie", "universe", None, dict(timeout=3)) # Comma separated list of privileges -> error. with pytest.raises( ReturnError, match=rf"unsupported privilege 'read,write', see pico.help\('{f}'\) for details", ): - i1.call(f"pico.{f}", "Dave", "read,write", "universe") + i1.call(f"pico.{f}", "Dave", "read,write", "universe", None, dict(timeout=3)) # No object_type -> error. with pytest.raises(ReturnError, match="object_type should be a string"): @@ -85,32 +85,32 @@ def test_acl_lua_api(cluster: Cluster): # No such object_type -> error. with pytest.raises(ReturnError, match="Unknown object type 'bible'"): - i1.call(f"pico.{f}", "Dave", "read", "bible") + i1.call(f"pico.{f}", "Dave", "read", "bible", None, dict(timeout=3)) # Wrong combo -> error. with pytest.raises(ReturnError, match="Unsupported space privilege 'grant'"): - i1.call(f"pico.{f}", "Dave", "grant", "space") + i1.call(f"pico.{f}", "Dave", "grant", "space", None, dict(timeout=3)) # No such role -> error. with pytest.raises(ReturnError, match="Role 'Joker' is not found"): - i1.call(f"pico.{f}", "Dave", "execute", "role", "Joker") + i1.call(f"pico.{f}", "Dave", "execute", "role", "Joker", dict(timeout=3)) # # pico.grant_privilege semantics verification # # Grant privilege to user -> Ok. - i1.call("pico.grant_privilege", "Dave", "read", "space", "_pico_property") + i1.call("pico.grant_privilege", "Dave", "read", "space", "_pico_property", dict(timeout=3)) # Already granted -> error. with pytest.raises( ReturnError, match="User 'Dave' already has read access on space '_pico_property'", ): - i1.call("pico.grant_privilege", "Dave", "read", "space", "_pico_property") + i1.call("pico.grant_privilege", "Dave", "read", "space", "_pico_property", dict(timeout=3)) # Grant privilege to role -> Ok. - i1.call("pico.grant_privilege", "Parent", "write", "space", "_pico_property") + i1.call("pico.grant_privilege", "Parent", "write", "space", "_pico_property", dict(timeout=3)) # Already granted -> error. # FIXME: tarantool says User instead of Role. @@ -118,33 +118,33 @@ def test_acl_lua_api(cluster: Cluster): ReturnError, match="User 'Parent' already has write access on space '_pico_property'", ): - i1.call("pico.grant_privilege", "Parent", "write", "space", "_pico_property") + i1.call("pico.grant_privilege", "Parent", "write", "space", "_pico_property", dict(timeout=3)) # Assign role to user -> Ok. - i1.call("pico.grant_privilege", "Dave", "execute", "role", "Parent") + i1.call("pico.grant_privilege", "Dave", "execute", "role", "Parent", dict(timeout=3)) # Already assigned role to user -> error. with pytest.raises( ReturnError, match="User 'Dave' already has execute access on role 'Parent'" ): - i1.call("pico.grant_privilege", "Dave", "execute", "role", "Parent") + i1.call("pico.grant_privilege", "Dave", "execute", "role", "Parent", dict(timeout=3)) # # pico.revoke_privilege semantics verification # # Revoke privilege to user -> Ok. - i1.call("pico.revoke_privilege", "Dave", "read", "space", "_pico_property") + i1.call("pico.revoke_privilege", "Dave", "read", "space", "_pico_property", dict(timeout=3)) # Already revoked -> error. with pytest.raises( ReturnError, match="User 'Dave' does not have read access on space '_pico_property'", ): - i1.call("pico.revoke_privilege", "Dave", "read", "space", "_pico_property") + i1.call("pico.revoke_privilege", "Dave", "read", "space", "_pico_property", dict(timeout=3)) # Revoke privilege to role -> Ok. - i1.call("pico.revoke_privilege", "Parent", "write", "space", "_pico_property") + i1.call("pico.revoke_privilege", "Parent", "write", "space", "_pico_property", dict(timeout=3)) # Already revoked -> error. # FIXME: tarantool says User instead of Role. @@ -152,16 +152,16 @@ def test_acl_lua_api(cluster: Cluster): ReturnError, match="User 'Parent' does not have write access on space '_pico_property'", ): - i1.call("pico.revoke_privilege", "Parent", "write", "space", "_pico_property") + i1.call("pico.revoke_privilege", "Parent", "write", "space", "_pico_property", dict(timeout=3)) # Revoke role to user -> Ok. - i1.call("pico.revoke_privilege", "Dave", "execute", "role", "Parent") + i1.call("pico.revoke_privilege", "Dave", "execute", "role", "Parent", dict(timeout=3)) # Already revoked role to user -> error. with pytest.raises( ReturnError, match="User 'Dave' does not have execute access on role 'Parent'" ): - i1.call("pico.revoke_privilege", "Dave", "execute", "role", "Parent") + i1.call("pico.revoke_privilege", "Dave", "execute", "role", "Parent", dict(timeout=3)) # # pico.drop_user @@ -169,18 +169,18 @@ def test_acl_lua_api(cluster: Cluster): # No user -> error. with pytest.raises(ReturnError, match="user should be a string"): - i1.call("pico.drop_user") + i1.call("pico.drop_user", dict(timeout=3)) # No such user -> error. with pytest.raises(ReturnError, match="User 'User is not found' is not found"): - i1.call("pico.drop_user", "User is not found") + i1.call("pico.drop_user", "User is not found", dict(timeout=3)) # Ok. - i1.call("pico.drop_user", "Dave") + i1.call("pico.drop_user", "Dave", dict(timeout=3)) # Repeat drop -> error. with pytest.raises(ReturnError, match="User 'Dave' is not found"): - i1.call("pico.drop_user", "Dave") + i1.call("pico.drop_user", "Dave", dict(timeout=3)) # # pico.drop_role @@ -192,14 +192,14 @@ def test_acl_lua_api(cluster: Cluster): # No such role -> error. with pytest.raises(ReturnError, match="Role 'Role is not found' is not found"): - i1.call("pico.drop_role", "Role is not found") + i1.call("pico.drop_role", "Role is not found", dict(timeout=3)) # Ok. - i1.call("pico.drop_role", "Parent") + i1.call("pico.drop_role", "Parent", dict(timeout=3)) # Repeat drop -> error. with pytest.raises(ReturnError, match="Role 'Parent' is not found"): - i1.call("pico.drop_role", "Parent") + i1.call("pico.drop_role", "Parent", dict(timeout=3)) # # Options validation @@ -219,6 +219,12 @@ def test_acl_lua_api(cluster: Cluster): ): i1.call("pico.create_user", "Dave", "pass", dict(timeout="3s")) + # No timeout -> error. + with pytest.raises( + ReturnError, match="opts.timeout is mandatory" + ): + i1.call("pico.create_user", "Dave", "pass") + def test_acl_basic(cluster: Cluster): i1, *_ = cluster.deploy(instance_count=4, init_replication_factor=2) @@ -235,7 +241,7 @@ def test_acl_basic(cluster: Cluster): # # # Create user. - index = i1.call("pico.create_user", user, password) + index = i1.call("pico.create_user", user, password, dict(timeout=3)) cluster.raft_wait_index(index) v += 1 @@ -270,11 +276,11 @@ def test_acl_basic(cluster: Cluster): # Grant some privileges. # Doing anything via remote function execution requires execute access # to the "universe" - index = i1.call("pico.grant_privilege", user, "execute", "universe") + index = i1.call("pico.grant_privilege", user, "execute", "universe", None, dict(timeout=3)) cluster.raft_wait_index(index) v += 1 - index = i1.call("pico.grant_privilege", user, "read", "space", "money") + index = i1.call("pico.grant_privilege", user, "read", "space", "money", dict(timeout=3)) cluster.raft_wait_index(index) v += 1 @@ -320,7 +326,7 @@ def test_acl_basic(cluster: Cluster): # # # Revoke the privilege. - index = i1.call("pico.revoke_privilege", user, "read", "space", "money") + index = i1.call("pico.revoke_privilege", user, "read", "space", "money", dict(timeout=3)) cluster.raft_wait_index(index) v += 1 @@ -337,7 +343,7 @@ def test_acl_basic(cluster: Cluster): # Change user's password. old_password = password new_password = "$3kr3T" - index = i1.call("pico.change_password", user, new_password) + index = i1.call("pico.change_password", user, new_password, dict(timeout=3)) cluster.raft_wait_index(index) v += 1 @@ -355,7 +361,7 @@ def test_acl_basic(cluster: Cluster): # # # Drop user. - index = i1.call("pico.drop_user", user) + index = i1.call("pico.drop_user", user, dict(timeout=3)) for i in cluster.instances: i.raft_wait_index(index) @@ -390,12 +396,12 @@ def test_acl_roles_basic(cluster: Cluster): password = "1234" # Create user. - index = i1.call("pico.create_user", user, password) + index = i1.call("pico.create_user", user, password, dict(timeout=3)) cluster.raft_wait_index(index) # Doing anything via remote function execution requires execute access # to the "universe" - index = i1.call("pico.grant_privilege", user, "execute", "universe") + index = i1.call("pico.grant_privilege", user, "execute", "universe", None, dict(timeout=3)) cluster.raft_wait_index(index) # Try reading from space on behalf of the user. @@ -410,15 +416,15 @@ def test_acl_roles_basic(cluster: Cluster): # # Create role. role = "PropertyReader" - index = i1.call("pico.create_role", role) + index = i1.call("pico.create_role", role, dict(timeout=3)) cluster.raft_wait_index(index) # Grant the role read access. - index = i1.call("pico.grant_privilege", role, "read", "space", "_pico_property") + index = i1.call("pico.grant_privilege", role, "read", "space", "_pico_property", dict(timeout=3)) cluster.raft_wait_index(index) # Assign role to user. - index = i1.call("pico.grant_privilege", user, "execute", "role", role) + index = i1.call("pico.grant_privilege", user, "execute", "role", role, dict(timeout=3)) cluster.raft_wait_index(index) # Try reading from space on behalf of the user again. Now succeed. @@ -427,7 +433,7 @@ def test_acl_roles_basic(cluster: Cluster): assert len(rows) > 0 # Revoke read access from the role. - index = i1.call("pico.revoke_privilege", role, "read", "space", "_pico_property") + index = i1.call("pico.revoke_privilege", role, "read", "space", "_pico_property", dict(timeout=3)) cluster.raft_wait_index(index) # Try reading from space on behalf of the user yet again, which fails again. @@ -439,7 +445,7 @@ def test_acl_roles_basic(cluster: Cluster): i.call("box.space._pico_property:select", user=user, password=password) # Drop the role. - index = i1.call("pico.drop_role", role) + index = i1.call("pico.drop_role", role, dict(timeout=3)) cluster.raft_wait_index(index) # Nothing changed here.