mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Added GRANT ROLE TO ROLE | USER functionality.
The command only currenty affects in memory data structures. Writing to the roles_mapping table needs to be implemented.
This commit is contained in:
parent
95ef78e432
commit
1ac0b920d5
5 changed files with 350 additions and 26 deletions
|
@ -6569,3 +6569,9 @@ ER_INVALID_ROLE
|
|||
ER_INVALID_CURRENT_USER
|
||||
eng "The current user is invalid."
|
||||
rum "Utilizatorul curent este invalid."
|
||||
ER_RESERVED_ROLE
|
||||
eng "Role name '%s' is reserved."
|
||||
rum "Numele de rol '%s' este rezervat."
|
||||
ER_CANNOT_GRANT_ROLE
|
||||
eng "Cannot grant role '%s' to: %s."
|
||||
rum "Rolul '%s' nu poate fi acordat catre: %s."
|
||||
|
|
309
sql/sql_acl.cc
309
sql/sql_acl.cc
|
@ -742,6 +742,9 @@ static my_bool acl_role_propagate_grants(ACL_ROLE *role,
|
|||
void * not_used __attribute__((unused)));
|
||||
static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping);
|
||||
|
||||
static void reset_role_db_privileges(ACL_ROLE *role);
|
||||
static void reset_role_table_and_column_privileges(ACL_ROLE *role);
|
||||
static void reset_role_routine_grant_privileges(ACL_ROLE *role);
|
||||
static void role_explore_create_list(ACL_ROLE *unused,
|
||||
ACL_ROLE *role,
|
||||
void *context_data);
|
||||
|
@ -751,6 +754,9 @@ static bool role_explore_merge_if_final(ACL_ROLE *current, ACL_ROLE *neighbour,
|
|||
static void role_explore_set_final_access_bits(ACL_ROLE *parent,
|
||||
ACL_ROLE *current,
|
||||
void *unused);
|
||||
static bool role_explore_detect_cycle(ACL_ROLE *current,
|
||||
ACL_ROLE *neighbour,
|
||||
void *context_data);
|
||||
static int traverse_role_graph(ACL_ROLE *role,
|
||||
void *context_data,
|
||||
bool (*on_start) (ACL_ROLE *role,
|
||||
|
@ -1926,17 +1932,11 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length,
|
|||
return (uchar*) buff->host.hostname;
|
||||
}
|
||||
|
||||
static void acl_update_role(const char *rolename,
|
||||
ulong privileges)
|
||||
{
|
||||
ACL_ROLE *role;
|
||||
mysql_mutex_assert_owner(&acl_cache->lock);
|
||||
|
||||
role= find_acl_role(rolename);
|
||||
if (!role)
|
||||
{
|
||||
return;
|
||||
}
|
||||
static void acl_update_role_entry(ACL_ROLE *role, ulong privileges)
|
||||
{
|
||||
|
||||
mysql_mutex_assert_owner(&acl_cache->lock);
|
||||
|
||||
/*
|
||||
Changing privileges of a role causes all other roles that had
|
||||
|
@ -1993,6 +1993,20 @@ static void acl_update_role(const char *rolename,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void acl_update_role(const char *rolename,
|
||||
ulong privileges)
|
||||
{
|
||||
mysql_mutex_assert_owner(&acl_cache->lock);
|
||||
ACL_ROLE *role= find_acl_role(rolename);
|
||||
if (!role)
|
||||
{
|
||||
return;
|
||||
}
|
||||
acl_update_role_entry(role, privileges);
|
||||
}
|
||||
|
||||
|
||||
static void acl_update_user(const char *user, const char *host,
|
||||
const char *password, uint password_len,
|
||||
enum SSL_type ssl_type,
|
||||
|
@ -2006,10 +2020,14 @@ static void acl_update_user(const char *user, const char *host,
|
|||
{
|
||||
mysql_mutex_assert_owner(&acl_cache->lock);
|
||||
|
||||
if (host[0] == '\0' && find_acl_role(user))
|
||||
if (host[0] == '\0')
|
||||
{
|
||||
acl_update_role(user, privileges);
|
||||
return;
|
||||
ACL_ROLE *acl_role= find_acl_role(user);
|
||||
if (acl_role)
|
||||
{
|
||||
acl_update_role_entry(acl_role, privileges);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i=0 ; i < acl_users.elements ; i++)
|
||||
|
@ -2071,6 +2089,12 @@ static void acl_insert_role(const char *rolename, ulong privileges)
|
|||
|
||||
mysql_mutex_assert_owner(&acl_cache->lock);
|
||||
entry= new (&mem) ACL_ROLE(rolename, privileges, &mem);
|
||||
(void) my_init_dynamic_array(&entry->parent_grantee,
|
||||
sizeof(ACL_USER_BASE *), 50, 100, MYF(0));
|
||||
(void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
|
||||
50, 100, MYF(0));
|
||||
|
||||
|
||||
|
||||
my_hash_insert(&acl_roles, (uchar *)entry);
|
||||
}
|
||||
|
@ -2416,6 +2440,7 @@ my_bool acl_user_reset_grant(ACL_USER *user,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void role_explore_create_list(ACL_ROLE *unused __attribute__((unused)),
|
||||
ACL_ROLE *role, void *context_data)
|
||||
{
|
||||
|
@ -2433,6 +2458,16 @@ static bool role_explore_start_access_check(ACL_ROLE *role,
|
|||
*/
|
||||
if (role->flags & ROLE_GRANTS_FINAL)
|
||||
return TRUE;
|
||||
/*
|
||||
This function is called when the node is first opened by DFS.
|
||||
If it's ROLE_GRANTS were not final, then it means that it's existing
|
||||
privilege entries should be placed on their initial grant access state.
|
||||
*/
|
||||
|
||||
reset_role_db_privileges(role);
|
||||
reset_role_table_and_column_privileges(role);
|
||||
reset_role_routine_grant_privileges(role);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -2464,6 +2499,13 @@ static void role_explore_set_final_access_bits(ACL_ROLE *parent,
|
|||
}
|
||||
}
|
||||
|
||||
static bool role_explore_detect_cycle(ACL_ROLE *unused __attribute__((unused)),
|
||||
ACL_ROLE *unused2 __attribute__((unused)),
|
||||
void *unused3 __attribute__((unused)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
The function scans through all roles granted to the role passed as argument
|
||||
and places the permissions in the access variable. The traverse method is
|
||||
|
@ -2560,7 +2602,6 @@ static int traverse_role_graph(ACL_ROLE *role,
|
|||
if (neighbour->flags & ROLE_VISITED)
|
||||
{
|
||||
DBUG_PRINT("info", ("Found cycle"));
|
||||
/* TODO the edge needs to be ignored */
|
||||
if (on_cycle && on_cycle(current, neighbour, context_data))
|
||||
{
|
||||
result= 2;
|
||||
|
@ -5102,6 +5143,155 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
|
|||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
static void append_user(String *str, const char *u, const char *h,
|
||||
bool handle_as_role)
|
||||
{
|
||||
if (str->length())
|
||||
str->append(',');
|
||||
str->append('\'');
|
||||
str->append(u);
|
||||
/* hostname part is not relevant for roles, it is always empty */
|
||||
if (!handle_as_role)
|
||||
{
|
||||
str->append(STRING_WITH_LEN("'@'"));
|
||||
str->append(h);
|
||||
}
|
||||
str->append('\'');
|
||||
}
|
||||
|
||||
bool mysql_grant_role(THD *thd, List <LEX_USER> &list)
|
||||
{
|
||||
DBUG_ENTER("mysql_grant_role");
|
||||
/*
|
||||
The first entry in the list is the granted role. Need at least two
|
||||
entries for the command to be valid
|
||||
*/
|
||||
DBUG_ASSERT(list.elements >= 2);
|
||||
bool result= 0;
|
||||
String wrong_users;
|
||||
LEX_USER *user, *granted_role;
|
||||
char *rolename;
|
||||
char *username;
|
||||
char *hostname;
|
||||
bool handle_as_role;
|
||||
ACL_ROLE *role, *role_as_user;
|
||||
|
||||
List_iterator <LEX_USER> user_list(list);
|
||||
granted_role= user_list++;
|
||||
if (granted_role == ¤t_role)
|
||||
{
|
||||
rolename= thd->security_ctx->priv_role;
|
||||
if (!rolename[0])
|
||||
{
|
||||
my_error(ER_RESERVED_ROLE, MYF(0), "NONE");
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rolename= granted_role->user.str;
|
||||
}
|
||||
|
||||
mysql_rwlock_wrlock(&LOCK_grant);
|
||||
mysql_mutex_lock(&acl_cache->lock);
|
||||
if (!(role= find_acl_role(rolename)))
|
||||
{
|
||||
my_error(ER_INVALID_ROLE, MYF(0), rolename);
|
||||
mysql_mutex_unlock(&acl_cache->lock);
|
||||
mysql_rwlock_unlock(&LOCK_grant);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
while ((user= user_list++))
|
||||
{
|
||||
handle_as_role= FALSE;
|
||||
/* current_role is treated slightly different */
|
||||
if (user == ¤t_role)
|
||||
{
|
||||
handle_as_role= TRUE;
|
||||
/* current_role is NONE */
|
||||
if (!thd->security_ctx->priv_role[0])
|
||||
{
|
||||
append_user(&wrong_users, "NONE", "", TRUE);
|
||||
result= 1;
|
||||
continue;
|
||||
}
|
||||
if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
|
||||
{
|
||||
append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE);
|
||||
result= 1;
|
||||
continue;
|
||||
}
|
||||
/* can not grant current_role to current_role */
|
||||
if (granted_role == ¤t_role)
|
||||
{
|
||||
append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE);
|
||||
result= 1;
|
||||
continue;
|
||||
}
|
||||
username= thd->security_ctx->priv_role;
|
||||
hostname= (char *)"";
|
||||
}
|
||||
else
|
||||
{
|
||||
username= user->user.str;
|
||||
hostname= user->host.str;
|
||||
if (hostname == HOST_NOT_SPECIFIED)
|
||||
{
|
||||
if ((role_as_user= find_acl_role(username)))
|
||||
{
|
||||
handle_as_role= TRUE;
|
||||
hostname= (char *)"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR *)
|
||||
alloc_root(&mem,
|
||||
sizeof(ROLE_GRANT_PAIR));
|
||||
|
||||
/* TODO write into roles_mapping table */
|
||||
init_role_grant_pair(&mem, mapping,
|
||||
username, hostname, rolename);
|
||||
int res= add_role_user_mapping(mapping);
|
||||
if (res == -1)
|
||||
{
|
||||
append_user(&wrong_users, username, hostname, handle_as_role);
|
||||
result= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if this grant would cause a cycle. It only needs to be run
|
||||
if we're granting a role to a role
|
||||
*/
|
||||
if (handle_as_role &&
|
||||
traverse_role_graph(role, NULL, NULL, NULL, role_explore_detect_cycle,
|
||||
NULL) == 2)
|
||||
{
|
||||
append_user(&wrong_users, username, hostname, TRUE);
|
||||
result= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only need to propagate grants when granting a role to a role */
|
||||
if (handle_as_role)
|
||||
{
|
||||
acl_update_role_entry(role_as_user, role_as_user->initial_role_access);
|
||||
}
|
||||
}
|
||||
mysql_mutex_unlock(&acl_cache->lock);
|
||||
mysql_rwlock_unlock(&LOCK_grant);
|
||||
|
||||
if (result)
|
||||
my_error(ER_CANNOT_GRANT_ROLE, MYF(0),
|
||||
rolename,
|
||||
wrong_users.c_ptr_safe());
|
||||
|
||||
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
|
||||
ulong rights, bool revoke_grant, bool is_proxy)
|
||||
|
@ -7187,6 +7377,96 @@ static int show_routine_grants(THD* thd,
|
|||
return error;
|
||||
}
|
||||
|
||||
static void reset_role_db_privileges(ACL_ROLE *role)
|
||||
{
|
||||
char *rolename= role->user.str;
|
||||
for (uint i=0 ; i < acl_dbs.elements; i++)
|
||||
{
|
||||
ACL_DB *acl_db= dynamic_element(&acl_dbs,i,ACL_DB*);
|
||||
if (acl_db->user && (!acl_db->host.hostname || !acl_db->host.hostname[0])
|
||||
&& (!strcmp(rolename, acl_db->user)))
|
||||
{
|
||||
acl_db->access= acl_db->initial_access;
|
||||
}
|
||||
/* this is only an inherited entry that needs to be removed */
|
||||
if (!acl_db->access)
|
||||
{
|
||||
delete_dynamic_element(&acl_dbs, i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_role_table_and_column_privileges(ACL_ROLE *role)
|
||||
{
|
||||
char *rolename= role->user.str;
|
||||
for (uint i=0 ; i < column_priv_hash.records ; i++)
|
||||
{
|
||||
GRANT_TABLE *grant_table= (GRANT_TABLE *)
|
||||
my_hash_element(&column_priv_hash, i);
|
||||
if (grant_table->user && (!grant_table->host.hostname ||
|
||||
!grant_table->host.hostname[0]) &&
|
||||
!strcmp(rolename, grant_table->user))
|
||||
{
|
||||
grant_table->privs= grant_table->init_privs;
|
||||
grant_table->cols= grant_table->init_cols;
|
||||
if (grant_table->privs | grant_table->cols)
|
||||
{
|
||||
for (uint j=0 ; j < grant_table->hash_columns.records ; j++)
|
||||
{
|
||||
GRANT_COLUMN *grant_column= (GRANT_COLUMN *)
|
||||
my_hash_element(&grant_table->hash_columns, j);
|
||||
if (grant_column->init_rights == 0)
|
||||
{
|
||||
my_hash_delete(&grant_table->hash_columns, (uchar *)grant_column);
|
||||
j--;
|
||||
}
|
||||
else
|
||||
{
|
||||
grant_column->rights= grant_column->init_rights;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* delete the record altogether as we have no privileges left */
|
||||
my_hash_delete(&column_priv_hash, (uchar *)grant_table);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_role_routine_grant_privileges(ACL_ROLE *role)
|
||||
{
|
||||
char *rolename= role->user.str;
|
||||
for (uint is_proc= 0; is_proc < 2; is_proc++) {
|
||||
HASH *hash;
|
||||
if (is_proc)
|
||||
hash= &proc_priv_hash;
|
||||
else
|
||||
hash= &func_priv_hash;
|
||||
|
||||
for (uint i=0 ; i < hash->records ; i++)
|
||||
{
|
||||
GRANT_NAME *grant_name= (GRANT_NAME *) my_hash_element(hash, i);
|
||||
if (grant_name->user && (!grant_name->host.hostname ||
|
||||
!grant_name->host.hostname[0]) &&
|
||||
!strcmp(rolename, grant_name->user))
|
||||
{
|
||||
if (grant_name->init_privs == 0)
|
||||
{
|
||||
my_hash_delete(hash, (uchar *)grant_name);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
grant_name->privs= grant_name->init_privs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Make a clear-text version of the requested privilege.
|
||||
*/
|
||||
|
@ -8322,7 +8602,6 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
|
|||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
static void append_user(String *str, LEX_USER *user, bool handle_as_role)
|
||||
{
|
||||
if (str->length())
|
||||
|
|
|
@ -203,6 +203,8 @@ int check_change_password(THD *thd, const char *host, const char *user,
|
|||
char *password, uint password_len);
|
||||
bool change_password(THD *thd, const char *host, const char *user,
|
||||
char *password);
|
||||
|
||||
bool mysql_grant_role(THD *thd, List<LEX_USER> &user_list);
|
||||
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
|
||||
ulong rights, bool revoke, bool is_proxy);
|
||||
int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
|
||||
|
|
|
@ -3866,9 +3866,9 @@ end_with_restore_list:
|
|||
else
|
||||
{
|
||||
/* Conditionally writes to binlog */
|
||||
res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
|
||||
lex->sql_command == SQLCOM_REVOKE,
|
||||
lex->type == TYPE_ENUM_PROXY);
|
||||
res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
|
||||
lex->sql_command == SQLCOM_REVOKE,
|
||||
lex->type == TYPE_ENUM_PROXY);
|
||||
}
|
||||
if (!res)
|
||||
{
|
||||
|
@ -3890,8 +3890,15 @@ end_with_restore_list:
|
|||
case SQLCOM_REVOKE_ROLE:
|
||||
case SQLCOM_GRANT_ROLE:
|
||||
{
|
||||
/* TODO Implement grant */
|
||||
my_ok(thd);
|
||||
/* TODO access check */
|
||||
|
||||
if (thd->security_ctx->user) // If not replication
|
||||
{
|
||||
if (!(res= mysql_grant_role(thd, lex->users_list)))
|
||||
my_ok(thd);
|
||||
}
|
||||
else
|
||||
my_ok(thd);
|
||||
break;
|
||||
}
|
||||
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
|
||||
|
|
|
@ -1570,7 +1570,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
|
||||
%type <symbol> keyword keyword_sp
|
||||
|
||||
%type <lex_user> user grant_user grant_role
|
||||
%type <lex_user> user grant_user grant_role user_or_role
|
||||
|
||||
%type <charset>
|
||||
opt_collate
|
||||
|
@ -1624,7 +1624,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
opt_option opt_place
|
||||
opt_attribute opt_attribute_list attribute column_list column_list_id
|
||||
opt_column_list grant_privileges grant_ident grant_list grant_option
|
||||
object_privilege object_privilege_list user_list rename_list
|
||||
object_privilege object_privilege_list user_list user_and_role_list
|
||||
rename_list
|
||||
clear_privileges flush_options flush_option
|
||||
opt_with_read_lock flush_options_list
|
||||
equal optional_braces
|
||||
|
@ -13208,6 +13209,16 @@ user:
|
|||
}
|
||||
;
|
||||
|
||||
user_or_role:
|
||||
user
|
||||
{
|
||||
$$=$1;
|
||||
}
|
||||
| CURRENT_ROLE optional_braces
|
||||
{
|
||||
$$= ¤t_role;
|
||||
}
|
||||
|
||||
/* Keyword that we allow for identifiers (except SP labels) */
|
||||
keyword:
|
||||
keyword_sp {}
|
||||
|
@ -14240,8 +14251,8 @@ revoke_command:
|
|||
lex->users_list.push_front ($3);
|
||||
lex->sql_command= SQLCOM_REVOKE;
|
||||
lex->type= TYPE_ENUM_PROXY;
|
||||
}
|
||||
| grant_role FROM grant_list
|
||||
}
|
||||
| grant_role FROM user_and_role_list
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
lex->sql_command= SQLCOM_REVOKE_ROLE;
|
||||
|
@ -14294,11 +14305,13 @@ grant_command:
|
|||
lex->sql_command= SQLCOM_GRANT;
|
||||
lex->type= TYPE_ENUM_PROXY;
|
||||
}
|
||||
| grant_role TO_SYM grant_list
|
||||
| grant_role TO_SYM user_and_role_list
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
lex->sql_command= SQLCOM_GRANT_ROLE;
|
||||
lex->type= 0;
|
||||
/* The first role is the one that is granted */
|
||||
if (Lex->users_list.push_front($1))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
|
||||
;
|
||||
|
@ -14333,6 +14346,10 @@ grant_role:
|
|||
system_charset_info, 0))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| CURRENT_ROLE optional_braces
|
||||
{
|
||||
$$=¤t_role;
|
||||
}
|
||||
;
|
||||
|
||||
opt_table:
|
||||
|
@ -14522,6 +14539,19 @@ grant_list:
|
|||
}
|
||||
;
|
||||
|
||||
user_and_role_list:
|
||||
user_or_role
|
||||
{
|
||||
if (Lex->users_list.push_back($1))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| user_and_role_list ',' user_or_role
|
||||
{
|
||||
if (Lex->users_list.push_back($3))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
;
|
||||
|
||||
via_or_with: VIA_SYM | WITH ;
|
||||
using_or_as: USING | AS ;
|
||||
|
||||
|
|
Loading…
Reference in a new issue