diff --git a/extra/schema_fill.lua b/extra/schema_fill.lua index a18adb532a2e1c1a7335b95a2c2ed60c986244df..126b3161aaa75569beb3a877a1924d36f0b4bc25 100644 --- a/extra/schema_fill.lua +++ b/extra/schema_fill.lua @@ -62,4 +62,10 @@ _index:insert{_cluster.id, 1, 'uuid', 'tree', 1, 1, 1, 'str'} _user:insert{GUEST, ADMIN, 'guest', 'user'} _user:insert{ADMIN, ADMIN, 'admin', 'user'} _user:insert{PUBLIC, ADMIN, 'public', 'role'} +-- grant admin access to the universe _priv:insert{1, 1, 'universe', 0, 7} +-- grant 'public' role access to 'box.schema.user.info' function +_func:insert{1, 1, 'box.schema.user.info', 1} +_priv:insert{1, 2, 'function', 1, 4} +-- grant 'guest' role 'public' +_priv:insert{1, 0, 'role', 2, 4} diff --git a/src/box/alter.cc b/src/box/alter.cc index 1f55dc9fa71d4652884b98f656827b3e2ad4906e..cfafda8de106173f9362a2e051837516f4130df2 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -1494,12 +1494,16 @@ priv_def_check(struct priv_def *priv) role ? role->name : int2str(priv->object_id)); } - /* Only the creator of the role can grant it. */ - if (role->owner != grantor->uid && grantor->uid != ADMIN) { + /* Only the creator of the role can grant or revoke it. + * Everyone can grant 'PUBLIC' role. + */ + if (role->owner != grantor->uid && grantor->uid != ADMIN && + (role->uid != PUBLIC || priv->access < PRIV_X)) { tnt_raise(ClientError, ER_ACCESS_DENIED, role->name, grantor->name); } - role_check(role, grantee); + /* Not necessary to do during revoke, but who cares. */ + role_check(grantee, role); } default: break; @@ -1555,10 +1559,8 @@ grant_or_revoke(struct priv_def *priv) default: break; } - if (access) { - access[grantee->auth_token].granted = priv->access; - access[grantee->auth_token].effective = priv->access; - } + if (access) + privilege_grant(grantee, access, priv->access); } /** A trigger called on rollback of grant, or on commit of revoke. */ diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap index b429bfee3548f5bf6563426246cbce7b6a7302b2..b05c412abd7caacb3a4f7d12befa761f39e198e8 100644 Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 694c77ba1c41e058bfb19aaf6057d8d3af710a2c..cf918b9f17058852e4a0f8ca511b6d3574160a2a 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -986,7 +986,9 @@ box.schema.user.create = function(name, opts) auth_mech_list["chap-sha1"] = box.schema.user.password(opts.password) end local _user = box.space[box.schema.USER_ID] - _user:auto_increment{session.uid(), name, 'user', auth_mech_list} + uid = _user:auto_increment{session.uid(), name, 'user', auth_mech_list}[1] + -- grant role 'public' to the user + box.schema.user.grant(uid, 'public') end box.schema.user.exists = function(name) @@ -1132,10 +1134,22 @@ box.schema.role.drop = function(name) end return box.schema.user.drop(name) end -box.schema.role.grant = function(user_name, role_name, grantor) - return box.schema.user.grant(user_name, 'execute', 'role', role_name, grantor) +box.schema.role.grant = function(user_name, privilege, object_type, + object_name, grantor) + local uid = user_resolve(user_name) + if uid == nil then + box.error(box.error.NO_SUCH_ROLE, user_name) + end + return box.schema.user.grant(user_name, privilege, object_type, + object_name, grantor) end -box.schema.role.revoke = function(user_name, role_name) - return box.schema.user.revoke(user_name, 'execute', 'role', role_name) +box.schema.role.revoke = function(user_name, privilege, object_type, + object_name) + local uid = user_resolve(user_name) + if uid == nil then + box.error(box.error.NO_SUCH_ROLE, user_name) + end + return box.schema.user.revoke(user_name, privilege, object_type, + object_name) end box.schema.role.info = box.schema.user.info diff --git a/src/box/user.cc b/src/box/user.cc index 61cf2a5f86ba4bec52f82be20fb7dd29472e2434..2b7ae6e5dc5c845ccad39980a12e4d9241ef0946 100644 --- a/src/box/user.cc +++ b/src/box/user.cc @@ -83,6 +83,15 @@ user_map_is_set(struct user_map *map, uint8_t auth_token) return map->m[idx] & (((umap_int_t) 1) << bit_no); } +static inline bool +user_map_is_empty(struct user_map *map) +{ + for (int i = 0; i < USER_MAP_SIZE; i++) + if (map->m[i]) + return false; + return true; +} + /** * Merge two sets of users: add all users from right argument * to the left one. @@ -291,7 +300,7 @@ user_cache_free() /** {{{ roles */ void -role_check(struct user *role, struct user *grantee) +role_check(struct user *grantee, struct user *role) { /* * Check that there is no loop from grantee to role: @@ -303,7 +312,7 @@ role_check(struct user *role, struct user *grantee) user_map_init(&transitive_closure); user_map_set(&transitive_closure, grantee->auth_token); struct user_map current_layer = transitive_closure; - while (true) { + while (! user_map_is_empty(¤t_layer)) { /* * As long as we're traversing a directed * acyclic graph, we're bound to end at some @@ -311,17 +320,12 @@ role_check(struct user *role, struct user *grantee) */ struct user_map next_layer; user_map_init(&next_layer); - bool found = false; struct user_map_iterator it; user_map_iterator_init(&it, ¤t_layer); struct user *user; - while ((user = user_map_iterator_next(&it))) { + while ((user = user_map_iterator_next(&it))) user_map_union(&next_layer, &user->users); - found = true; - } user_map_union(&transitive_closure, &next_layer); - if (! found) - break; current_layer = next_layer; } @@ -336,18 +340,59 @@ void role_grant(struct user *grantee, struct user *role) { user_map_set(&role->users, grantee->auth_token); + /** + * Todo: grant all effective privileges of + * the role to whoever this role was granted + * to. + */ } void role_revoke(struct user *grantee, struct user *role) { user_map_clear(&role->users, grantee->auth_token); + /** + * Todo: rebuild effective privileges of grantee, + * for all effective privileges which he/she + * might have inherited through the revoked role. + */ } void -user_set_effective_access(struct user * /* grantee */, - struct user * /* role */) +privilege_grant(struct user *user, + struct access *object, uint8_t access) { + bool grant = access > object[user->auth_token].granted; + object[user->auth_token].granted = access; + if (grant) { + /* + * Grant the privilege to this user or + * role and all users to which this + * role has been granted, if this is + * a role. + */ + struct user_map current_layer; + user_map_init(¤t_layer); + user_map_set(¤t_layer, user->auth_token); + while (!user_map_is_empty(¤t_layer)) { + struct user_map next_layer; + user_map_init(&next_layer); + struct user_map_iterator it; + user_map_iterator_init(&it, ¤t_layer); + while ((user = user_map_iterator_next(&it))) { + object[user->auth_token].effective |= access; + user_map_union(&next_layer, &user->users); + } + current_layer = next_layer; + } + } else { + /** + * @fixme: this only works for users and + * non-recursive roles + */ + object[user->auth_token].effective = + object[user->auth_token].granted; + } } /** }}} */ diff --git a/src/box/user.h b/src/box/user.h index dfb0d551ab2931f2d79369a2a0705699f084a44e..5bfb4b63794b62cb167c8f341c82d630eb139fe1 100644 --- a/src/box/user.h +++ b/src/box/user.h @@ -124,7 +124,7 @@ user_cache_free(); * a given role. */ void -role_check(struct user *role, struct user *grantee); +role_check(struct user *grantee, struct user *role); /** * Grant a role to a user or another role. @@ -139,12 +139,13 @@ void role_revoke(struct user *grantee, struct user *role); /** - * Re-evaluate effective access of a user based on this role - * that is granted to him/her. - * The role has its effective access already re-evaluated. + * Grant or revoke a single privilege to a user or role + * and re-evaluate effective access of all users of this + * role if this role. */ void -user_set_effective_access(struct user *grantee, struct user *role); +privilege_grant(struct user *grantee, struct access *object, + uint8_t access); /* }}} */ diff --git a/test/box/access.result b/test/box/access.result index 56ff7199447ce591bf06a8b9b6ad6d8385507fa5..975ddea6cc2c035cb9871db15e6664b6f844c0a8 100644 --- a/test/box/access.result +++ b/test/box/access.result @@ -117,6 +117,9 @@ box.space['_user']:delete{uid} box.schema.user.revoke('rich', 'read,write', 'universe') --- ... +box.schema.user.revoke('rich', 'public') +--- +... box.space['_user']:delete{uid} --- - [4, 1, 'rich', 'user', []] @@ -352,42 +355,47 @@ box.schema.user.grant('user', 'read,write', 'universe') ... box.space._priv:select{id} --- -- - [1, 3, 'universe', 0, 3] +- - [1, 3, 'role', 2, 4] + - [1, 3, 'universe', 0, 3] ... box.schema.user.grant('user', 'read', 'universe') --- ... box.space._priv:select{id} --- -- - [1, 3, 'universe', 0, 3] +- - [1, 3, 'role', 2, 4] + - [1, 3, 'universe', 0, 3] ... box.schema.user.revoke('user', 'write', 'universe') --- ... box.space._priv:select{id} --- -- - [1, 3, 'universe', 0, 1] +- - [1, 3, 'role', 2, 4] + - [1, 3, 'universe', 0, 1] ... box.schema.user.revoke('user', 'read', 'universe') --- ... box.space._priv:select{id} --- -- [] +- - [1, 3, 'role', 2, 4] ... box.schema.user.grant('user', 'write', 'universe') --- ... box.space._priv:select{id} --- -- - [1, 3, 'universe', 0, 2] +- - [1, 3, 'role', 2, 4] + - [1, 3, 'universe', 0, 2] ... box.schema.user.grant('user', 'read', 'universe') --- ... box.space._priv:select{id} --- -- - [1, 3, 'universe', 0, 3] +- - [1, 3, 'role', 2, 4] + - [1, 3, 'universe', 0, 3] ... box.schema.user.drop('user') --- diff --git a/test/box/access.test.lua b/test/box/access.test.lua index bdbc654a41470bcbba9c67b1984b94330b9c19ae..f0cd9f6d2e938193cc7ba62c86e1df6e292226f8 100644 --- a/test/box/access.test.lua +++ b/test/box/access.test.lua @@ -56,6 +56,7 @@ box.space['_user']:delete{uid} box.schema.func.drop('dummy') box.space['_user']:delete{uid} box.schema.user.revoke('rich', 'read,write', 'universe') +box.schema.user.revoke('rich', 'public') box.space['_user']:delete{uid} box.schema.user.drop('test') diff --git a/test/box/access_misc.result b/test/box/access_misc.result index fedbfeb0f1702f37efcc2b2790482cfbd832b776..27e7d7afb79cdceb16323e25ce36051465f2b08f 100644 --- a/test/box/access_misc.result +++ b/test/box/access_misc.result @@ -580,7 +580,7 @@ box.space._space:select() ... box.space._func:select() --- -- [] +- - [1, 1, 'box.schema.user.info', 1] ... session = nil --- diff --git a/test/box/bootstrap.result b/test/box/bootstrap.result index 670fab9ff86cb24ef0e53e7f1c082742492de37f..d42ed4f1823a39acc9b6088e2caf080b5a1b13ee 100644 --- a/test/box/bootstrap.result +++ b/test/box/bootstrap.result @@ -83,9 +83,11 @@ box.space._user:select{} ... box.space._func:select{} --- -- [] +- - [1, 1, 'box.schema.user.info', 1] ... box.space._priv:select{} --- -- - [1, 1, 'universe', 0, 7] +- - [1, 0, 'role', 2, 4] + - [1, 1, 'universe', 0, 7] + - [1, 2, 'function', 1, 4] ... diff --git a/test/box/role.result b/test/box/role.result index 3114636230cd695cd1fe10911ecda9c767a59a94..ea2e0877d5819397d6471b4456a91e2b5fa31681 100644 --- a/test/box/role.result +++ b/test/box/role.result @@ -23,15 +23,15 @@ box.session.su('iddqd') -- test granting privilege to a role box.schema.role.grant('iddqd', 'execute', 'universe') --- -- error: Role 'execute' is not found ... box.schema.role.info('iddqd') --- -- [] +- - - execute + - universe + - ... box.schema.role.revoke('iddqd', 'execute', 'universe') --- -- error: Role 'execute' is not found ... box.schema.role.info('iddqd') --- @@ -43,7 +43,9 @@ box.schema.user.create('tester') ... box.schema.user.info('tester') --- -- [] +- - - execute + - role + - public ... box.schema.user.grant('tester', 'execute', 'role', 'iddqd') --- @@ -51,6 +53,9 @@ box.schema.user.grant('tester', 'execute', 'role', 'iddqd') box.schema.user.info('tester') --- - - - execute + - role + - public + - - execute - role - iddqd ... @@ -154,3 +159,79 @@ box.schema.user.info('b') box.schema.role.drop('b') --- ... +-- check a grant received via a role +box.schema.user.create('test') +--- +... +box.schema.user.create('grantee') +--- +... +box.schema.role.create('liaison') +--- +... +box.schema.user.grant('grantee', 'liaison') +--- +... +box.schema.user.grant('test', 'read,write', 'universe') +--- +... +box.session.su('test') +--- +... +s = box.schema.space.create('test') +--- +... +s:create_index('i1') +--- +- unique: true + parts: + - type: NUM + fieldno: 1 + id: 0 + space_id: 512 + name: i1 + type: TREE +... +box.schema.role.grant('liaison', 'read,write', 'space', 'test') +--- +... +box.session.su('grantee') +--- +... +box.space.test:insert{1} +--- +- [1] +... +box.space.test:select{1} +--- +- - [1] +... +box.session.su('test') +--- +... +box.schema.user.revoke('liaison', 'read,write', 'space', 'test') +--- +... +box.session.su('grantee') +--- +... +box.space.test:insert{1} +--- +- error: Duplicate key exists in unique index 0 +... +box.space.test:select{1} +--- +- - [1] +... +box.session.su('admin') +--- +... +box.schema.user.drop('test') +--- +... +box.schema.user.drop('grantee') +--- +... +box.schema.user.drop('liaison') +--- +... diff --git a/test/box/role.test.lua b/test/box/role.test.lua index 33e5039248f7d7c502439571190213985a9e9226..5ec9efe38e7385c0141b72729e21d140e3753e7a 100644 --- a/test/box/role.test.lua +++ b/test/box/role.test.lua @@ -50,3 +50,26 @@ box.schema.user.grant('b', 'a') box.schema.role.drop('a') box.schema.user.info('b') box.schema.role.drop('b') +-- check a grant received via a role +box.schema.user.create('test') +box.schema.user.create('grantee') +box.schema.role.create('liaison') +box.schema.user.grant('grantee', 'liaison') +box.schema.user.grant('test', 'read,write', 'universe') +box.session.su('test') +s = box.schema.space.create('test') +s:create_index('i1') +box.schema.role.grant('liaison', 'read,write', 'space', 'test') +box.session.su('grantee') +box.space.test:insert{1} +box.space.test:select{1} +box.session.su('test') +box.schema.user.revoke('liaison', 'read,write', 'space', 'test') +box.session.su('grantee') +box.space.test:insert{1} +box.space.test:select{1} +box.session.su('admin') +box.schema.user.drop('test') +box.schema.user.drop('grantee') +box.schema.user.drop('liaison') + diff --git a/test/wal/func_max.result b/test/wal/func_max.result index 88f4d84ab146f3007f77415ea05162831b93704d..f52366ec990e37f98c0662532c9873ef5ffceeb4 100644 --- a/test/wal/func_max.result +++ b/test/wal/func_max.result @@ -33,7 +33,7 @@ func_limit(); ... drop_limit_func(); --- -- error: Function 'func32001' does not exist +- error: Function 'func32000' does not exist ... box.schema.user.create('testuser'); --- @@ -50,7 +50,7 @@ func_limit(); ... drop_limit_func(); --- -- error: Function 'func32001' does not exist +- error: Function 'func32000' does not exist ... session.su('admin') box.schema.user.drop('testuser');