Commit 2fced9e7 authored by Vicențiu Ciorbaru's avatar Vicențiu Ciorbaru

MDEV-13655: Set role does not properly grant privileges.

When granting a role to another role, DB privileges get propagated. If
the grantee had no previous DB privileges, an extra ACL_DB entry is created to
house those "indirectly received" privileges. If, afterwards, DB
privileges are granted to the grantee directly, we must make sure to not
create a duplicate ACL_DB entry.
parent 40088bfc
#
# MDEV-13655: SET ROLE does not properly grant privileges.
#
# We must test that if aditional db privileges get granted to a role
# which previously inherited privileges from another granted role
# keep the internal memory structures intact.
#
create role simple;
#
# First we create an entry with privileges for databases for the simple role.
#
grant select, insert, update, delete, lock tables, execute on t.* to simple;
create role admin;
#
# Now we grant the simple role to admin. This means that db privileges
# should propagate to admin.
#
grant simple to admin;
show grants for admin;
Grants for admin
GRANT simple TO 'admin'
GRANT USAGE ON *.* TO 'admin'
GRANT USAGE ON *.* TO 'simple'
GRANT SELECT, INSERT, UPDATE, DELETE, LOCK TABLES, EXECUTE ON `t`.* TO 'simple'
#
# Finally, we give the admin all the available privileges for the db.
#
grant all on t.* to admin;
#
# Create a user to test out the new roles;
#
create user foo;
grant admin to foo;
create database t;
ERROR 42000: Access denied for user 'foo'@'%' to database 't'
set role admin;
show grants;
Grants for foo@%
GRANT admin TO 'foo'@'%'
GRANT USAGE ON *.* TO 'foo'@'%'
GRANT simple TO 'admin'
GRANT USAGE ON *.* TO 'admin'
GRANT ALL PRIVILEGES ON `t`.* TO 'admin'
GRANT USAGE ON *.* TO 'simple'
GRANT SELECT, INSERT, UPDATE, DELETE, LOCK TABLES, EXECUTE ON `t`.* TO 'simple'
create database t;
drop database t;
drop role simple;
drop role admin;
drop user foo;
source include/not_embedded.inc;
--echo #
--echo # MDEV-13655: SET ROLE does not properly grant privileges.
--echo #
--echo # We must test that if aditional db privileges get granted to a role
--echo # which previously inherited privileges from another granted role
--echo # keep the internal memory structures intact.
--echo #
create role simple;
--echo #
--echo # First we create an entry with privileges for databases for the simple role.
--echo #
grant select, insert, update, delete, lock tables, execute on t.* to simple;
create role admin;
--echo #
--echo # Now we grant the simple role to admin. This means that db privileges
--echo # should propagate to admin.
--echo #
grant simple to admin;
show grants for admin;
--echo #
--echo # Finally, we give the admin all the available privileges for the db.
--echo #
grant all on t.* to admin;
--echo #
--echo # Create a user to test out the new roles;
--echo #
create user foo;
grant admin to foo;
connect (foo,localhost,foo,,,,,);
--error ER_DBACCESS_DENIED_ERROR
create database t;
set role admin;
show grants;
create database t;
drop database t;
connection default;
drop role simple;
drop role admin;
drop user foo;
...@@ -2129,37 +2129,42 @@ static void acl_insert_user(const char *user, const char *host, ...@@ -2129,37 +2129,42 @@ static void acl_insert_user(const char *user, const char *host,
} }
static void acl_update_db(const char *user, const char *host, const char *db, static bool acl_update_db(const char *user, const char *host, const char *db,
ulong privileges) ulong privileges)
{ {
mysql_mutex_assert_owner(&acl_cache->lock); mysql_mutex_assert_owner(&acl_cache->lock);
bool updated= false;
for (uint i=0 ; i < acl_dbs.elements ; i++) for (uint i=0 ; i < acl_dbs.elements ; i++)
{ {
ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
if ((!acl_db->user && !user[0]) || if ((!acl_db->user && !user[0]) ||
(acl_db->user && (acl_db->user &&
!strcmp(user,acl_db->user))) !strcmp(user,acl_db->user)))
{ {
if ((!acl_db->host.hostname && !host[0]) || if ((!acl_db->host.hostname && !host[0]) ||
(acl_db->host.hostname && (acl_db->host.hostname &&
!strcmp(host, acl_db->host.hostname))) !strcmp(host, acl_db->host.hostname)))
{ {
if ((!acl_db->db && !db[0]) || if ((!acl_db->db && !db[0]) ||
(acl_db->db && !strcmp(db,acl_db->db))) (acl_db->db && !strcmp(db,acl_db->db)))
{ {
if (privileges) if (privileges)
{ {
acl_db->access= privileges; acl_db->access= privileges;
acl_db->initial_access= acl_db->access; acl_db->initial_access= acl_db->access;
} }
else else
delete_dynamic_element(&acl_dbs,i); delete_dynamic_element(&acl_dbs,i);
} updated= true;
}
} }
} }
} }
return updated;
} }
...@@ -3428,9 +3433,21 @@ static int replace_db_table(TABLE *table, const char *db, ...@@ -3428,9 +3433,21 @@ static int replace_db_table(TABLE *table, const char *db,
acl_cache->clear(1); // Clear privilege cache acl_cache->clear(1); // Clear privilege cache
if (old_row_exists) if (old_row_exists)
acl_update_db(combo.user.str,combo.host.str,db,rights); acl_update_db(combo.user.str,combo.host.str,db,rights);
else else if (rights)
if (rights) {
acl_insert_db(combo.user.str,combo.host.str,db,rights); /*
If we did not have an already existing row, for users, we must always
insert an ACL_DB entry. For roles however, it is possible that one was
already created when DB privileges were propagated from other granted
roles onto the current role. For this case, first try to update the
existing entry, otherwise insert a new one.
*/
if (!combo.is_role() ||
!acl_update_db(combo.user.str, combo.host.str, db, rights))
{
acl_insert_db(combo.user.str,combo.host.str,db,rights);
}
}
DBUG_RETURN(0); DBUG_RETURN(0);
/* This could only happen if the grant tables got corrupted */ /* This could only happen if the grant tables got corrupted */
......
...@@ -191,7 +191,7 @@ typedef int *(*update_var)(THD *, struct st_mysql_show_var *); ...@@ -191,7 +191,7 @@ typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
typedef struct st_lex_user { typedef struct st_lex_user {
LEX_STRING user, host, password, plugin, auth; LEX_STRING user, host, password, plugin, auth;
bool is_role() { return user.str[0] && !host.str[0]; } bool is_role() const { return user.str[0] && !host.str[0]; }
void set_lex_string(LEX_STRING *l, char *buf) void set_lex_string(LEX_STRING *l, char *buf)
{ {
if (is_role()) if (is_role())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment