Hi, Oleksandr,
> commit 5154e224a97
> Author: Oleksandr Byelkin <sanja(a)mariadb.com>
> Date: Mon Dec 13 16:15:21 2021 +0100
>
> diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql
> index fd2f1c95dda..a1721913b2c 100644
> --- a/scripts/mysql_system_tables.sql
> +++ b/scripts/mysql_system_tables.sql
> @@ -47,6 +47,10 @@ INSERT IGNORE INTO global_priv SELECT * FROM tmp_user_sys WHERE 0 <> @need_sys_u
> DROP TABLE tmp_user_sys;
>
>
> +-- This special "role" needed for GRAND ... TO PUBLIC
> +INSERT IGNORE INTO global_priv (Host,User,Priv) VALUES ('', 'PUBLIC', concat('{"access":0,"version_id":',regexp_replace(regexp_replace(@@version, '\\b\\d\\b', '0\\0'), '\\D', ''),',"is_role":true}'));
1. why here and not in mysql_system_tables_data.sql ?
2. why this version magic? mysql_system_tables_data.sql doesn't do it for other accounts
3. why do you do it at all? I thought the server can create this role as needed
> +
> +
> CREATE DEFINER='mariadb.sys'@'localhost' SQL SECURITY DEFINER VIEW IF NOT EXISTS user AS SELECT
> Host,
> User,
> @@ -95,7 +99,9 @@ CREATE DEFINER='mariadb.sys'@'localhost' SQL SECURITY DEFINER VIEW IF NOT EXISTS
> ELT(IFNULL(JSON_VALUE(Priv, '$.is_role'), 0) + 1, 'N', 'Y') AS is_role,
> IFNULL(JSON_VALUE(Priv, '$.default_role'), '') AS default_role,
> CAST(IFNULL(JSON_VALUE(Priv, '$.max_statement_time'), 0.0) AS DECIMAL(12,6)) AS max_statement_time
> - FROM global_priv;
> + FROM global_priv
> +-- Do not show special role for GRANT TO PUBLIC
> + WHERE not (Host = "" and User = "PUBLIC");
Why? I don't think it's wrong to show a new PUBLIC role.
>
> -- Remember for later if user table already existed
> set @had_user_table= @@warning_count != 0;
> diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
> index 601cae2e945..aa9bd91ec52 100644
> --- a/sql/sql_acl.cc
> +++ b/sql/sql_acl.cc
> @@ -316,6 +318,13 @@ static bool show_table_and_column_privileges(THD *, const char *, const char *,
> static int show_routine_grants(THD *, const char *, const char *,
> const Sp_handler *sph, char *, int);
>
> +static ACL_ROLE *acl_public= NULL;
> +
> +inline privilege_t public_access()
> +{
> + return (acl_public ? acl_public->access : NO_ACL);
> +}
Why not to have acl_public always not null?
e.g. define it as
static ACL_ROLE acl_public;
so it's not even a pointer. And don't store it in the dynamic array.
Or make it like
static ACL_ROLE acl_public_dummy, *acl_public= &acl_public_dummy;
then you can keep he real role in the dynamic array, but still
acl_public will never be null.
but in the standard PUBLIC is not a role at all, so may be it'd be
simpler not to store it in the dynamic array
> +
> class Grant_tables;
> class User_table;
> class Proxies_priv_table;
> @@ -3148,7 +3183,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
> }
> else // Role, not User
> {
> - ACL_ROLE *acl_role= find_acl_role(user);
> + ACL_ROLE *acl_role= find_acl_role(user, false);
are there tests for that?
> if (acl_role)
> {
> res= 0;
> @@ -3161,6 +3196,26 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
> }
> }
>
> + /*
> + PUBLIC magic:
> +
> + Note: for usual user privileges of 3 component merged together:
> + 1) user privileges
> + 2) set role privileges
> + 3) public privileges
> + But for this routine (used in Security_context::change_security_context)
> + only 2 component merged:
> + 1) user OR role privileges we are switching to
because if we're switching to a user, there can be no set role?
I feel this comment is more confusing, then helping, really,
I'd suggest to remove it completely
> + 2) public privileges
> + */
> + if (acl_public)
> + {
> + if (ACL_DB *acl_db= acl_db_find(db, public_name.str, "", "", FALSE))
> + sctx->db_access|= acl_db->access;
> +
> + sctx->master_access|= acl_public->access;
> + }
> +
> mysql_mutex_unlock(&acl_cache->lock);
> DBUG_RETURN(res);
> }
> @@ -3320,20 +3375,22 @@ int acl_setrole(THD *thd, const char *rolename, privilege_t access)
> /* merge the privileges */
> Security_context *sctx= thd->security_ctx;
> sctx->master_access= access;
> - if (thd->db.str)
> - sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
> -
> if (!strcasecmp(rolename, "NONE"))
> {
> thd->security_ctx->priv_role[0]= 0;
> }
> else
> {
> - if (thd->db.str)
> - sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
> /* mark the current role */
> strmake_buf(thd->security_ctx->priv_role, rolename);
> }
> + if (thd->db.str)
> + sctx->db_access= acl_get_all3(sctx, thd->db.str, FALSE);
> +
> + // PUBLIC magic
> + if (acl_public)
> + sctx->master_access|= acl_public->access;
why not to do it in check_user_can_set_role()?
Like
- *access = acl_user->access | role->access;
+ *access = acl_user->access | role->access | acl_public->access;
> +
> return 0;
> }
>
> @@ -3347,9 +3404,13 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length,
>
> static void acl_update_role(const char *rolename, const privilege_t privileges)
> {
> - ACL_ROLE *role= find_acl_role(rolename);
> + ACL_ROLE *role= find_acl_role(rolename, true);
> if (role)
> + {
> role->initial_role_access= role->access= privileges;
> + if (strcasecmp(rolename, public_name.str) == 0)
> + acl_public= role;
why? I mean, in what case can acl_public != role here?
> + }
> }
>
>
> @@ -3638,6 +3703,23 @@ privilege_t acl_get(const char *host, const char *ip,
> DBUG_RETURN(db_access & host_access);
> }
>
> +/*
> + Check if there is access for the host/user, role, public on the database
> +*/
> +
> +privilege_t acl_get_all3(Security_context *sctx, const char *db,
> + bool db_is_patern)
> +{
> + privilege_t access= acl_get(sctx->host, sctx->ip,
> + sctx->priv_user, db, db_is_patern);
> + if (sctx->priv_role[0])
> + access|= acl_get("", "", sctx->priv_role, db, db_is_patern);
> + if (acl_public)
> + access|= acl_get("", "", public_name.str, db, db_is_patern);
how can this be optimized, considering that in most cases and for most db
names there will be no grants to role and public?
A couple of ideas: maintain counters total_number_of_db_grants_to_roles
ad total_number_of_db_grants_to_public. This is rather crude.
keep grants to users, to roles, and to public in separate caches.
or in different dynamic arrays (won't help much in a default setup)
keep propery per ACL_DB or shared between all ACL_DB's of one DB?
that'd be rather tricky to maintain
> + return access;
> +}
> +
> +
> /*
> Check if there are any possible matching entries for this host
>
> @@ -3759,7 +3841,7 @@ static bool add_role_user_mapping(const char *uname, const char *hname,
> const char *rname)
> {
> ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
> - ACL_ROLE *role= find_acl_role(rname);
> + ACL_ROLE *role= find_acl_role(rname, true);
why?
>
> if (grantee == NULL || role == NULL)
> return 1;
> @@ -4247,7 +4329,7 @@ bool is_acl_user(const char *host, const char *user)
> if (*host) // User
> res= find_user_exact(host, user) != NULL;
> else // Role
> - res= find_acl_role(user) != NULL;
> + res= find_acl_role(user, false) != NULL;
this disallows DEFINER PUBLIC, right? I couldn't find a test for that.
>
> mysql_mutex_unlock(&acl_cache->lock);
> return res;
> @@ -7537,11 +7620,11 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
> if (user->host.str)
> hostname= user->host;
> else
> - if ((role_as_user= find_acl_role(user->user.str)))
> + if ((role_as_user= find_acl_role(user->user.str, false)))
Why is it false here? It looks like it disallows GRANT role TO PUBLIC
> hostname= empty_clex_str;
> else
> {
> - if (is_invalid_role_name(username.str))
> + if (check_role_name(username.str, false) == ROLE_NAME_INVALID)
> {
> append_user(thd, &wrong_users, &username, &empty_clex_str);
> result= 1;
> @@ -8280,19 +8355,14 @@ bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
> if (any_combination_will_do)
> continue;
>
> - t_ref->grant.grant_table_user= grant_table; // Remember for column test
> - t_ref->grant.grant_table_role= grant_table_role;
> - t_ref->grant.version= grant_version;
> - t_ref->grant.privilege|= grant_table ? grant_table->privs : NO_ACL;
> - t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : NO_ACL;
> + t_ref->grant.privilege|= t_ref->grant.aggregate_privs();
> t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
>
> if (!(~t_ref->grant.privilege & want_access))
> continue;
>
> - if ((want_access&= ~((grant_table ? grant_table->cols : NO_ACL) |
> - (grant_table_role ? grant_table_role->cols : NO_ACL) |
> - t_ref->grant.privilege)))
> + if ((want_access&= ~(t_ref->grant.aggregate_cols() |
> + t_ref->grant.privilege)))
> {
> goto err; // impossible
> }
1. why is this if() here, if it's impossible?
2. I'm a bit worried that you set t_ref->grant.version early when it's
still possible to goto err. Old code was setting the version very late
after all checks have succeeded.
> @@ -8335,6 +8405,49 @@ static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
> }
> }
>
> +inline privilege_t GRANT_INFO::aggregate_privs()
> +{
> + return (grant_table_user ? grant_table_user->privs : NO_ACL) |
> + (grant_table_role ? grant_table_role->privs : NO_ACL) |
> + (grant_public ? grant_public->privs : NO_ACL);
> +}
> +
> +inline privilege_t GRANT_INFO::aggregate_cols()
> +{
> + return (grant_table_user ? grant_table_user->cols : NO_ACL) |
> + (grant_table_role ? grant_table_role->cols : NO_ACL) |
> + (grant_public ? grant_public->cols : NO_ACL);
> +}
> +
> +void GRANT_INFO::refresh(const Security_context *sctx,
> + const char *db, const char *table)
> +{
> + if (version != grant_version)
> + read(sctx, db, table);
> +}
> +
> +void GRANT_INFO::read(const Security_context *sctx,
> + const char *db, const char *table)
> +{
> +#ifdef EMBEDDED_LIBRARY
> + grant_table_user= grant_table_role= grant_public= NULL;
> +#else
> + grant_table_user=
> + table_hash_search(sctx->host, sctx->ip, db,
> + sctx->priv_user,
> + table, FALSE); /* purecov: inspected */
you don't need to preserve purecov comments when moving the code around
> + grant_table_role=
> + sctx->priv_role[0] ? table_hash_search("", NULL, db,
> + sctx->priv_role,
> + table, TRUE) : NULL;
> + grant_public=
> + acl_public ? table_hash_search("", NULL, db,
> + public_name.str,
> + table, TRUE) : NULL;
> +#endif
> + version= grant_version; /* purecov: inspected */
> +}
> +
> /*
> Check column rights in given security context
>
> @@ -9533,6 +9626,13 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
> want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
> else
> want_access= acl_entry->access;
> +
> + // suppress "GRANT USAGE ON *.* TO `PUBLIC`"
why? I agree that it's pointless, but disabling just it
is kind of difficult to explain.
> + if (!(want_access & ~GRANT_ACL) &&
> + acl_entry->user.length == public_name.length &&
> + strcasecmp(acl_entry->user.str, public_name.str) == 0)
> + return FALSE;
> +
> if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
> global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
> else if (!(want_access & ~GRANT_ACL))
> @@ -11015,10 +11116,15 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
> {
> int rc;
> user_name= get_current_user(thd, tmp_user_name, false);
> - if (!user_name)
> + if (!user_name || (handle_as_role &&
> + (strcasecmp(user_name->user.str,
> + public_name.str) == 0)))
better add this condition to the following if(), not to this one.
Like:
- if (handle_as_role != user_name->is_role())
+ if (handle_as_role != user_name->is_role() ||
+ (handle_as_role && check_role_name(...) == ROLE_NAME_INVALID))
> {
> thd->clear_error();
> - append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
> + if (!user_name)
> + append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
> + else
> + append_str(&wrong_users, public_name.str, public_name.length);
> result= TRUE;
> continue;
> }
> diff --git a/sql/sql_show.cc b/sql/sql_show.cc
> index 6290a47ee30..143e647f74c 100644
> --- a/sql/sql_show.cc
> +++ b/sql/sql_show.cc
> @@ -5304,7 +5300,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
> &thd->col_access, NULL, 0, 1) ||
> (!thd->col_access && check_grant_db(thd, db_name->str))) ||
> sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
> - acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0))
> + acl_get_all3(sctx, db_name->str, 0))
Was it a bug? That one couldn't see db's if privileges were granted
via roles?
> #endif
> {
> Dynamic_array<LEX_CSTRING*> table_names(PSI_INSTRUMENT_MEM);
> diff --git a/mysql-test/main/public_basic.test b/mysql-test/main/public_basic.test
> new file mode 100644
> index 00000000000..3f5993dfe97
> --- /dev/null
> +++ b/mysql-test/main/public_basic.test
> @@ -0,0 +1,93 @@
> +SHOW GRANTS FOR PUBLIC;
> +
> +--echo # it is not PUBLIC but an user
> +--echo # (this should work as it allowed for roles for example)
> +create user PUBLIC;
> +create user PUBLIC@localhost;
> +GRANT SELECT on test.* to PUBLIC@localhost;
> +drop user PUBLIC@localhost;
> +drop user PUBLIC;
> +
> +--echo # preinstalled PUBLIC
> +GRANT SELECT on test.* to PUBLIC;
> +GRANT SELECT on mysql.db to PUBLIC;
> +--replace_regex /"version_id"\:[0-9]+/"version_id":VERSION/
> +select * from mysql.global_priv where user="PUBLIC" ;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +GRANT UPDATE on test.* to PUBLIC;
> +GRANT UPDATE on mysql.db to PUBLIC;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +REVOKE SELECT on test.* from PUBLIC;
> +REVOKE SELECT on mysql.db from PUBLIC;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +REVOKE UPDATE on test.* from PUBLIC;
> +REVOKE UPDATE on mysql.db from PUBLIC;
> +
> +--error ER_NONEXISTING_GRANT
> +REVOKE UPDATE on test.* from PUBLIC;
> +--error ER_NONEXISTING_TABLE_GRANT
> +REVOKE UPDATE on mysql.db from PUBLIC;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +--echo # automaticly added PUBLIC
> +delete from mysql.global_priv where user="PUBLIC";
> +flush privileges;
> +select * from mysql.global_priv where user="PUBLIC" ;
> +GRANT SELECT on test.* to PUBLIC;
> +GRANT SELECT on mysql.db to PUBLIC;
> +--replace_regex /"version_id"\:[0-9]+/"version_id":VERSION/
> +select * from mysql.global_priv where user="PUBLIC" ;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +GRANT UPDATE on test.* to PUBLIC;
> +GRANT UPDATE on mysql.db to PUBLIC;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +REVOKE SELECT on test.* from PUBLIC;
> +REVOKE SELECT on mysql.db from PUBLIC;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +REVOKE UPDATE on test.* from PUBLIC;
> +REVOKE UPDATE on mysql.db from PUBLIC;
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +--error ER_INVALID_ROLE
> +GRANT XXXXXX TO CURRENT_USER;
> +--echo # following should fail with the same error as above
> +--error ER_INVALID_ROLE
> +GRANT PUBLIC TO CURRENT_USER;
> +
> +--error ER_INVALID_ROLE
> +REVOKE XXXXXX FROM CURRENT_USER;
> +--echo # following should fail with the same error as above
> +--error ER_INVALID_ROLE
> +REVOKE PUBLIC FROM CURRENT_USER;
> +--error ER_CANNOT_USER
> +
> +drop role XXXXXX;
> +--echo # following should fail with the same error as above
> +--error ER_CANNOT_USER
> +drop role PUBLIC;
> +
> +--error ER_INVALID_ROLE
> +SET ROLE XXXXXX;
> +--echo # following should fail with the same error as above
> +--error ER_INVALID_ROLE
> +SET ROLE PUBLIC;
> +
> +--error ER_INVALID_ROLE
> +SET DEFAULT ROLE XXXXXX;
> +--echo # following should fail with the same error as above
> +--error ER_INVALID_ROLE
> +SET DEFAULT ROLE PUBLIC;
This is good, but one can update global_priv table directly
and put the default_role=PUBLIC there. Please, add a test for that.
and also a couple of tests for `public` (in backticks)
> diff --git a/mysql-test/main/public_privileges.test b/mysql-test/main/public_privileges.test
> new file mode 100644
> index 00000000000..a542376f05c
> --- /dev/null
> +++ b/mysql-test/main/public_privileges.test
> @@ -0,0 +1,293 @@
> +--echo #
> +--echo # Test DB/TABLE/COLUMN privileges in queries
> +--echo #
> +
> +SHOW GRANTS FOR PUBLIC;
> +
> +create user testuser;
> +create database testdb1;
> +use testdb1;
> +create table t1 (a int, b int);
> +insert into t1 values (1,2);
> +create database testdb2;
> +use testdb2;
> +create table t2 (a int, b int);
> +insert into t2 values (1,2);
> +create table t3 (a int, b int);
> +insert into t3 values (1,2);
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
"connect testuser" also switches to this connection,
the following "connection testuser" is redundant
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select * from testdb1.t1;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select * from testdb2.t2;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select b from testdb2.t3;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select a from testdb2.t3;
> +
> +connection default;
> +
> +GRANT SELECT ON testdb1.* to PUBLIC;
> +GRANT SELECT ON testdb2.t2 to PUBLIC;
> +GRANT SELECT (b) ON testdb2.t3 to PUBLIC;
> +
> +disconnect testuser;
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +select * from testdb1.t1;
> +select * from testdb2.t2;
> +select b from testdb2.t3;
> +--error ER_COLUMNACCESS_DENIED_ERROR
> +select a from testdb2.t3;
> +
> +connection default;
> +
> +--replace_column 1 # 3 # 6 # 7 #
> +SHOW PROCESSLIST;
why?
> +
> +connection default;
> +
> +use test;
> +disconnect testuser;
> +REVOKE SELECT ON testdb1.* from PUBLIC;
> +REVOKE SELECT ON testdb2.t2 from PUBLIC;
> +REVOKE SELECT (b) ON testdb2.t3 from PUBLIC;
may be `revoke all privileges, grant option` ?
looks like a good place to have it tested
> +drop user testuser;
> +drop database testdb1;
> +drop database testdb2;
> +
> +--echo #
> +--echo # test global process list privilege and EXECUTE db level
> +--echo #
> +
> +create user testuser;
> +create database testdb;
> +use testdb;
> +create procedure p1 () select 1;
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +--replace_column 1 # 3 # 6 # 7 #
> +SHOW PROCESSLIST;
> +--error ER_PROCACCESS_DENIED_ERROR
> +call testdb.p1();
> +
> +connection default;
> +
> +GRANT PROCESS ON *.* to PUBLIC;
> +GRANT EXECUTE ON testdb.* to PUBLIC;
> +
> +disconnect testuser;
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +--replace_column 1 # 3 # 6 # 7 #
> +SHOW PROCESSLIST;
> +call testdb.p1();
> +
> +connection default;
> +
> +--replace_column 1 # 3 # 6 # 7 #
> +SHOW PROCESSLIST;
> +
> +connection default;
> +
> +use test;
> +disconnect testuser;
> +REVOKE PROCESS ON *.* from PUBLIC;
> +REVOKE EXECUTE ON testdb.* from PUBLIC;
> +drop user testuser;
> +drop database testdb;
> +
> +--echo #
> +--echo # test DB privilege to allow USE statement
> +--echo #
> +
> +create user testuser;
> +create database testdb;
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +--error ER_DBACCESS_DENIED_ERROR
> +use testdb;
> +
> +connection default;
> +
> +GRANT LOCK TABLES ON testdb.* to PUBLIC;
> +
> +disconnect testuser;
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +use testdb;
> +
> +connection default;
> +
> +use test;
> +disconnect testuser;
> +REVOKE LOCK TABLES ON testdb.* from PUBLIC;
> +drop user testuser;
> +drop database testdb;
> +
> +
> +--echo #
> +--echo # test DB privilege to allow USE statement (as above)
> +--echo # test current db privileges
> +--echo #
> +
> +create user testuser;
> +create database testdb;
> +use testdb;
> +create table t1 (a int);
> +insert into t1 values (1);
> +GRANT LOCK TABLES ON testdb.* to PUBLIC;
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +use testdb;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +update t1 set a=a+1;
> +
> +connection default;
> +
> +GRANT UPDATE,SELECT ON testdb.* to PUBLIC;
> +
> +disconnect testuser;
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +use testdb;
> +update t1 set a=a+1;
> +
> +connection default;
> +select * from testdb.t1;
> +
> +use test;
> +disconnect testuser;
> +REVOKE LOCK TABLES ON testdb.* from PUBLIC;
> +REVOKE UPDATE,SELECT ON testdb.* from PUBLIC;
> +drop user testuser;
> +drop database testdb;
> +
> +
> +--echo #
> +--echo # test DB privilege to allow USE statement (as above)
> +--echo # test table/column privileges in current DB
> +--echo #
> +
> +create user testuser;
> +create database testdb;
> +use testdb;
> +create table t1 (a int);
> +insert into t1 values (1);
> +create table t2 (a int, b int);
> +insert into t2 values (1,2);
> +GRANT LOCK TABLES ON testdb.* to PUBLIC;
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +use testdb;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +delete from t1;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select b from t2;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select a from t2;
> +
> +connection default;
> +
> +GRANT DELETE ON testdb.t1 to PUBLIC;
> +GRANT SELECT (a) ON testdb.t2 to PUBLIC;
> +
> +disconnect testuser;
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +use testdb;
> +delete from t1;
> +select a from t2;
> +--error ER_COLUMNACCESS_DENIED_ERROR
> +select b from t2;
> +
> +connection default;
> +select * from testdb.t1;
> +
> +use test;
> +disconnect testuser;
> +REVOKE LOCK TABLES ON testdb.* from PUBLIC;
> +REVOKE DELETE ON testdb.t1 from PUBLIC;
> +REVOKE SELECT (a) ON testdb.t2 from PUBLIC;
> +drop user testuser;
> +drop database testdb;
> +
> +--echo #
> +--echo # test function privilege
> +--echo #
> +
> +create user testuser;
> +create database testdb;
> +use testdb;
> +create function f1() returns int return 2;
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +--error ER_PROCACCESS_DENIED_ERROR
> +alter function testdb.f1 comment "A stupid function";
> +--error ER_PROCACCESS_DENIED_ERROR
> +select testdb.f1();
> +
> +connection default;
> +
> +GRANT ALTER ROUTINE ON testdb.* to PUBLIC;
> +
> +disconnect testuser;
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +
> +alter function testdb.f1 comment "A stupid function";
> +--error ER_PROCACCESS_DENIED_ERROR
> +select testdb.f1();
> +
> +connection default;
> +
> +use test;
> +disconnect testuser;
> +REVOKE ALTER ROUTINE ON testdb.* from PUBLIC;
> +drop function testdb.f1;
> +drop user testuser;
> +drop database testdb;
> +
> +--echo #
> +--echo # bug with automatically added PUBLIC role
> +--echo #
> +
> +--echo # automaticly added PUBLIC
> +delete from mysql.global_priv where user="PUBLIC";
> +flush privileges;
> +GRANT SELECT on test.* to PUBLIC;
> +
> +REVOKE SELECT on test.* from PUBLIC;
> +
> +create user testuser;
> +create database testdb1;
> +use testdb1;
> +create table t1 (a int, b int);
> +insert into t1 values (1,2);
> +
> +connect (testuser,localhost,testuser,,);
> +connection testuser;
> +--error ER_TABLEACCESS_DENIED_ERROR
> +select * from testdb1.t1;
> +
> +connection default;
> +
> +disconnect testuser;
> +drop user testuser;
> +drop database testdb1;
> diff --git a/mysql-test/suite/roles/none_public.test b/mysql-test/suite/roles/none_public.test
> index a0ec2315cfc..d0ba62d226b 100644
> --- a/mysql-test/suite/roles/none_public.test
> +++ b/mysql-test/suite/roles/none_public.test
> @@ -19,8 +19,9 @@ grant select on *.* to none;
> grant public to role1;
> --error ER_INVALID_ROLE
> grant role1 to public;
^^^
this shouldn't fail
> ---error ER_INVALID_ROLE
> -grant select on *.* to public;
> +# PUBLIC is legal role
> +#--error ER_INVALID_ROLE
> +#grant select on *.* to public;
you can just remove it, commenting out isn't better
> --error ER_INVALID_ROLE
> grant role1 to current_role;
> @@ -40,16 +41,18 @@ revoke select on *.* from public;
>
> --error ER_INVALID_ROLE
> show grants for none;
> ---error ER_INVALID_ROLE
> -show grants for public;
> +# PUBLIC is legal role
> +#--error ER_INVALID_ROLE
> +#show grants for public;
>
> --error ER_INVALID_ROLE
> create definer=none view test.v1 as select 1;
> ---error ER_INVALID_ROLE
> -create definer=public view test.v1 as select 1;
> +# PUBLIC is legal role
> +#--error ER_INVALID_ROLE
> +#create definer=public view test.v1 as select 1;
eh, no, PUBLIC isn't a valid definer, as far as I understand.
>
> drop role role1;
>
> -insert mysql.global_priv values ('', 'none', '{"is_role":true}'), ('', 'public', '{"is_role":true}');
> +insert mysql.global_priv values ('', 'none', '{"is_role":true}');
> flush privileges;
> -delete from mysql.global_priv where host='';
> +delete from mysql.global_priv where host='' and user='none';
> diff --git a/mysql-test/main/sp_notembedded.result b/mysql-test/main/sp_notembedded.result
> index e03361598a6..239f8acb7f3 100644
> --- a/mysql-test/main/sp_notembedded.result
> +++ b/mysql-test/main/sp_notembedded.result
> @@ -345,7 +345,6 @@ show grants;
> Grants for foo1@localhost
> GRANT USAGE ON *.* TO `foo1`@`localhost` IDENTIFIED BY PASSWORD '-F3A2A51A9B0F2BE2468926B4132313728C250DBF'
> GRANT CREATE ROUTINE ON `test`.* TO `foo1`@`localhost`
> -GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE `test`.`spfoo` TO `foo1`@`localhost`
Why did this line disappear?
> connection default;
> disconnect foo;
> drop procedure spfoo;
Regards,
Sergei
VP of MariaDB Server Engineering
and security(a)mariadb.org