summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorVicențiu Ciorbaru <cvicentiu@gmail.com>2013-10-18 06:49:53 -0700
committerSergei Golubchik <sergii@pisem.net>2013-10-18 06:49:53 -0700
commit1ac0b920d572ec393a2b482b6fa0686a6708abdd (patch)
tree4b62d53027d9a759cd4dc2e5a844afb0f6a739d2 /sql
parent95ef78e432e66f9b851a81a6878b11f9cc55532f (diff)
downloadmariadb-git-1ac0b920d572ec393a2b482b6fa0686a6708abdd.tar.gz
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.
Diffstat (limited to 'sql')
-rw-r--r--sql/share/errmsg-utf8.txt6
-rw-r--r--sql/sql_acl.cc307
-rw-r--r--sql/sql_acl.h2
-rw-r--r--sql/sql_parse.cc17
-rw-r--r--sql/sql_yacc.yy42
5 files changed, 349 insertions, 25 deletions
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index a6e3a74df0a..9b937c67316 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -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."
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 700e2d38783..9716f7c251c 100644
--- a/sql/sql_acl.cc
+++ b/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)
+
+static void acl_update_role_entry(ACL_ROLE *role, ulong privileges)
{
- ACL_ROLE *role;
- mysql_mutex_assert_owner(&acl_cache->lock);
- role= find_acl_role(rolename);
- if (!role)
- {
- return;
- }
+ 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 == &current_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 == &current_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 == &current_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())
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 1340d6fc2e7..8a2054cee90 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -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,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index d30d476543d..9d17d9c3e31 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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*/
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 92770c6aacd..38f9709f180 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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
+ {
+ $$= &current_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
+ {
+ $$=&current_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 ;