diff options
author | Vicențiu Ciorbaru <cvicentiu@gmail.com> | 2013-10-18 06:49:53 -0700 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2013-10-18 06:49:53 -0700 |
commit | 1ac0b920d572ec393a2b482b6fa0686a6708abdd (patch) | |
tree | 4b62d53027d9a759cd4dc2e5a844afb0f6a739d2 /sql | |
parent | 95ef78e432e66f9b851a81a6878b11f9cc55532f (diff) | |
download | mariadb-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.txt | 6 | ||||
-rw-r--r-- | sql/sql_acl.cc | 307 | ||||
-rw-r--r-- | sql/sql_acl.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 17 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 42 |
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 == ¤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()) 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 + { + $$= ¤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 ; |