Following up on MySQL security: inconsistencies, and on MySQL bug #61596, I was thinking it may take a long time till the non-existent ROUTINE_PRIVILEGES view is implemented. Here’s my own implementation of the view.
I’ve followed the somewhat strange conventions used in the *_PRIVILEGES tables in INFORMATION_SCHEMA, where the IS_GRANTABLE is a separate column, although in 2nd 1st normal form.
I present it here as a query, using session variables, rather than a view definition:
SELECT STRAIGHT_JOIN
CONCAT('\'', User, '\'@\'', Host, '\'') AS GRANTEE,
NULL AS ROUTINE_CATALOG,
Db AS ROUTINE_SCHEMA,
Routine_name AS ROUTINE_NAME,
Routine_type AS ROUTINE_TYPE,
UPPER(SUBSTRING_INDEX(SUBSTRING_INDEX(Proc_priv, ',', n+1), ',', -1)) AS PRIVILEGE_TYPE,
IF(grantable_procs_priv.User IS NULL, 'NO', 'YES') AS IS_GRANTABLE
FROM
mysql.procs_priv
CROSS JOIN (SELECT @counter := -1) select_init
CROSS JOIN (
SELECT
@counter := @counter+1 AS n
FROM
INFORMATION_SCHEMA.COLLATIONS
LIMIT 5
) numbers
LEFT JOIN (
SELECT
DISTINCT User, Host, Db, Routine_name
FROM
mysql.procs_priv
WHERE
find_in_set('Grant', Proc_priv) > 0
) grantable_procs_priv USING (User, Host, Db, Routine_name)
WHERE
numbers.n BETWEEN 0 AND CHAR_LENGTH(Proc_priv) - CHAR_LENGTH(REPLACE(Proc_priv, ',', ''))
AND UPPER(SUBSTRING_INDEX(SUBSTRING_INDEX(Proc_priv, ',', n+1), ',', -1)) != 'GRANT'
ORDER BY
GRANTEE, ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, n
;
It takes 2 views and a table to make this a VIEW rather than a query.
First teaser: the view which represents this query, along with many other interesting diagnostic views, is to take part in a new open source project I’m working on.
[UPDATE]
Guess I was in a rush to produce the query. Here’s a shorter, cleaner one:
SELECT
CONCAT('\'', User, '\'@\'', Host, '\'') AS GRANTEE,
NULL AS ROUTINE_CATALOG,
Db AS ROUTINE_SCHEMA,
Routine_name AS ROUTINE_NAME,
Routine_type AS ROUTINE_TYPE,
UPPER(SUBSTRING_INDEX(SUBSTRING_INDEX(Proc_priv, ',', n+1), ',', -1)) AS PRIVILEGE_TYPE,
IF(find_in_set('Grant', Proc_priv) > 0, 'YES', 'NO') AS IS_GRANTABLE
FROM
mysql.procs_priv
CROSS JOIN (
SELECT
@counter := @counter+1 AS n
FROM
INFORMATION_SCHEMA.COLLATIONS, (SELECT @counter := -1) select_init
LIMIT 5
) numbers
WHERE
numbers.n BETWEEN 0 AND CHAR_LENGTH(Proc_priv) - CHAR_LENGTH(REPLACE(Proc_priv, ',', ''))
AND UPPER(SUBSTRING_INDEX(SUBSTRING_INDEX(Proc_priv, ',', n+1), ',', -1)) != 'GRANT'
ORDER BY
GRANTEE, ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, n
;
Example output:
+--------------------------+-----------------+----------------+----------------------------+--------------+----------------+--------------+ | GRANTEE | ROUTINE_CATALOG | ROUTINE_SCHEMA | ROUTINE_NAME | ROUTINE_TYPE | PRIVILEGE_TYPE | IS_GRANTABLE | +--------------------------+-----------------+----------------+----------------------------+--------------+----------------+--------------+ | 'other_user'@'localhost' | NULL | sakila | film_in_stock | PROCEDURE | EXECUTE | NO | | 'other_user'@'localhost' | NULL | sakila | film_in_stock | PROCEDURE | ALTER ROUTINE | NO | | 'other_user'@'localhost' | NULL | sakila | get_customer_balance | FUNCTION | EXECUTE | NO | | 'other_user'@'localhost' | NULL | sakila | get_customer_balance | FUNCTION | ALTER ROUTINE | NO | | 'other_user'@'localhost' | NULL | sakila | inventory_held_by_customer | FUNCTION | EXECUTE | NO | | 'other_user'@'localhost' | NULL | sakila | inventory_held_by_customer | FUNCTION | ALTER ROUTINE | NO | | 'shlomi'@'127.0.0.1' | NULL | sakila | film_in_stock | PROCEDURE | EXECUTE | YES | | 'shlomi'@'127.0.0.1' | NULL | sakila | get_customer_balance | FUNCTION | EXECUTE | NO | | 'shlomi'@'127.0.0.1' | NULL | sakila | get_customer_balance | FUNCTION | ALTER ROUTINE | NO | | 'world_user'@'localhost' | NULL | sakila | get_customer_balance | FUNCTION | EXECUTE | YES | | 'world_user'@'localhost' | NULL | sakila | get_customer_balance | FUNCTION | ALTER ROUTINE | YES | +--------------------------+-----------------+----------------+----------------------------+--------------+----------------+--------------+
Hi Shlomi,
A nitpick: 2nd normal form requires that each attribute be dependent on “the whole key”; so actually, the fact that the attribute is_grantable depends only on part of the key makes the table _not_ 2NF (and consequently, of course, not any higher NF).
(according to Chris Date, the fact that these tables contain nulls makes them not even 1NF, but I believe most people disagree there).
Shai,
Thanks. Please continue nitpicking