Privilege for CREATE AGGREGATE FUNCTION - mysql

Why am I unable to create this aggregate function?
As far as I can tell, I've granted the appropriate privileges (CREATE ROUTINE and ALTER ROUTINE) both on *.* and on my database (peacock):
mysql> SELECT User, Host, Create_priv, Create_routine_priv, Alter_routine_priv FROM mysql.user WHERE user='glpy';
+------+-----------+-------------+---------------------+--------------------+
| User | Host | Create_priv | Create_routine_priv | Alter_routine_priv |
+------+-----------+-------------+---------------------+--------------------+
| glpy | localhost | Y | Y | Y |
+------+-----------+-------------+---------------------+--------------------+
mysql> SELECT User, Host, db, Create_priv, Create_routine_priv, Alter_routine_priv FROM mysql.db WHERE user='glpy';
+------+-----------+---------+-------------+---------------------+--------------------+
| User | Host | db | Create_priv | Create_routine_priv | Alter_routine_priv |
+------+-----------+---------+-------------+---------------------+--------------------+
| glpy | localhost | peacock | N | Y | Y |
+------+-----------+---------+-------------+---------------------+--------------------+
However, when I try to create the function, I get Access denied ... to database 'mysql':
mysql> use peacock;
mysql> CREATE AGGREGATE FUNCTION coverage RETURNS INT SONAME 'my-library.so';
ERROR 1044 (42000): Access denied for user 'glpy'#'localhost' to database 'mysql'
In particular:
Why is access required to the mysql table to create the aggregate function?
Given that I have granted the privileges on *.*, why is access denied?
I also tried granting the privileges on mysql, but access was still denied.

CREATE [AGGREGATE] FUNCTION function_name
RETURNS {STRING|INTEGER|REAL|DECIMAL}
SONAME shared_library_name;
This is the declaration for a User Defined Function, which appears to be what you are trying to install. This is not declaring a Stored Function. CREATE FUNCTION has two different uses in MySQL. This is one of them.
For this to work:
you do not need the CREATE ROUTINE privilege
it does not matter what database you're in, because User-Defined Functions, unlike stored functions, are global in scope. They extend MySQL functionality in a way that is analogous to its built-in aggregate functions, like AVG() and SUM() and MIN() and MAX().
you cannot specify a database as part of the function declaration
the following is the requirement that you are missing:
13.7.3.1. CREATE FUNCTION Syntax for User-Defined Functions
To create a function, you must have the INSERT privilege for the mysql database. This is necessary because CREATE FUNCTION adds a row to the mysql.func system table that records the function's name, type, and shared library name.
— http://dev.mysql.com/doc/refman/5.6/en/create-function-udf.html
If you don't have this privilege, the function will have to be installed by someone who holds this privilege, or it will need to be granted to you. You can't grant it to yourself.

From the manual:
CREATE PROCEDURE and CREATE FUNCTION require the CREATE ROUTINE privilege.
Make sure the privilege is on the database that you are trying to use. When you run CREATE AGGREGATE FUNCTION coverage RETURNS INT SONAME 'my-library.so';, you are trying to create the function in the mysql database (that is, the system database that is actually named mysql), so you need the CREATE ROUTINE privilege on that database. If you are trying to create it in some other database, run a USE my_database command or modify your syntax:
CREATE AGGREGATE FUNCTION my_database.coverage RETURNS INTEGER SONAME 'my-library.so';
If you are in a shared environment (pretty much anything other than a VPS or dedicated server), you probably don't have access to do anything to the mysql database and never will. So, create the function in your own database.
Edit: INT should be INTEGER.

Related

Query to retrieve list of privileges in different database

Is there a query (not a stored procedure or a command) which can be executed to retrieve list of privileges for objects (Tables, Views, Stored Procedures, Functions & Triggers) and users for a particular database (require following columns -- Schema name, Object type, Object name, Permission)?
MySQL --
Tried this but require a consolidate query -- SHOW GRANTS FOR 'root'#'localhost';
Oracle --
Tried this SELECT * FROM DBA_TAB_PRIVS but it provides for tables and views only
MariaDB --
SQL --
As of Oracle (I don't know other databases; by the way, I believe you wrongly used the sql tag. It is the language, while the database you're probably talking about is named the MS SQL Server), remember that you can ask the Dictionary. For example:
SQL> select * From dictionary where lower(comments) like '%grant%';
TABLE_NAME COMMENTS
-------------------- -------------------------------------------------------
<snip>
USER_ROLE_PRIVS Roles granted to current user
USER_SYS_PRIVS System privileges granted to current user
USER_TAB_PRIVS Grants on objects for which the user is the owner, gran
tor or grantee
USER_TAB_PRIVS_MADE All grants on objects owned by the user
USER_TAB_PRIVS_RECD Grants on objects for which the user is the grantee
<snip>
20 rows selected.
SQL>
Saying that DBA_TAB_PRIVS (which displays info for the whole database; I'm running this from an ordinary user, not a DBA) shows only tables and views, well - you are wrong. It displays procedures as well. How do I know?
This is my procedure and I'll grant execute privilege to mike:
SQL> select object_name, object_type from user_procedures where object_name = 'P_TEST';
OBJECT_NAME OBJECT_TYPE
--------------- -------------------
P_TEST PROCEDURE
SQL> grant execute on p_test to mike;
Grant succeeded.
What do I see?
SQL> select grantee, owner, table_name, privilege
2 from user_tab_privs
3 where table_name = 'P_TEST';
GRANTEE OWNER TABLE_NAME PRIVILEGE
---------- ---------- -------------------- ----------
MIKE SCOTT P_TEST EXECUTE
SQL>
Here it is. So yes, you were wrong.

MySQL user and host precedence

I have two users, let's call them foo on two different databases:
Database_A:
mysql> select user,host from user where user = 'foo';
+-----------+-----------+
| user | host |
+-----------+-----------+
| foo | % |
| foo | 10.% |
| foo | localhost |
+-----------+-----------+
3 rows in set (0.01 sec)
Database_B:
mysql> select user,host from user where user = 'foo';
+-----------+-----------+
| user | host |
+-----------+-----------+
| foo | % |
| foo | 10.% |
| foo | localhost |
+-----------+-----------+
3 rows in set (0.00 sec)
Now the issue I am running into is trying to run a SQL script w/ the DROP command. When I connect to the database on Database_A, I get an error trying to connect:
mysql -A -hdatabase_1.foo.bar.domain.com -ufoo -pbar Database_A <dbtables.sql
Warning: Using a password on the command line interface can be insecure.
ERROR 1142 (42000) at line 22: DROP command denied to user 'foo'#'ip-10-128-0-143.ec2.internal' for table 'bar_table'
I can run this on the Database_B w/ no issues. So far, I have checked with the grants on both users and have logged into the MySQL shell w/ the same user and grants (foo#'10.%') but I can't run the SQL script on database_1. Here are what the grants look like for both Database_A and Database_B:
mysql> show grants for foo;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grants for foo#% |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, RELOAD ON *.* TO 'foo'#'%' IDENTIFIED BY PASSWORD '<redacted>' REQUIRE SSL |
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE ROUTINE, ALTER ROUTINE ON `reference`.* TO 'foo'#'%' |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> show grants for foo#'10.%';
+------------------------------------------------------------------------------------------------------------+
| Grants for foo#10.% |
+------------------------------------------------------------------------------------------------------------+
| GRANT FILE ON *.* TO 'foo'#'10.%' IDENTIFIED BY PASSWORD '<redacted>' |
+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
Now I can't seem to figure out why one database server would work vs the other one (both are set up exactly the same w/ the user and in the my.cnf). Is there a way where I can log in specifically as foo#'%'? I am trying to run this from a remote EC2 instance (same VPC):
mysql -A -hdatabase_1.foo.bar.domain.com -ufoo -pbar Database_A <dbtables.sql
No, you can't force connection as a particular user profile. MySQL uses the first matching profile, according to its sort order.
Read https://dev.mysql.com/doc/refman/8.0/en/connection-access.html:
When multiple matches are possible, the server must determine which of
them to use. It resolves this issue as follows:
Whenever the server reads the user table into memory, it sorts the rows.
When a client attempts to connect, the server looks through the rows in sorted order.
The server uses the first row that matches the client host name and user name.
The server uses sorting rules that order rows with the most-specific
Host values first. Literal host names and IP addresses are the most
specific. (The specificity of a literal IP address is not affected by
whether it has a netmask, so 198.51.100.13 and
198.51.100.0/255.255.255.0 are considered equally specific.) The pattern '%' means “any host” and is least specific. The empty string
'' also means “any host” but sorts after '%'. Rows with the same Host
value are ordered with the most-specific User values first (a blank
User value means “any user” and is least specific). For rows with
equally-specific Host and User values, the order is nondeterministic.
I would avoid giving different privileges to the same username, differing only by the hostname.
Even though MySQL allows you to define different privileges depending on the client host, it's confusing to manage your authorizations this way. I've never seen a good reason to do that.
If you need a distinct set of privileges, define a distinct username.

Grant Execute Function permission to another user

I have a database function fn_relation_isModerator, only user api has access to this function. Now I want another user to have this permission (while keeping the previous permission as well).
I checked routine name and user by following query:
select routine_name, routine_type, definer from information_schema.ROUTINES where ROUTINE_SCHEMA = 'db_name';
Which resulted:
+-------------------------+---------------+----------+
| ROUTINE_NAME | ROUTINE_TYPE | DEFINER |
+-------------------------+---------------+----------+
| | | |
| fn_relation_isModerator | FUNCTION | api#% |
+-------------------------+---------------+----------+
Approach 1:
So I ran the following query to grant this permission:
GRANT EXECUTE ON PROCEDURE db_name.fn_relation_isModerator TO 'api_worker'#'%';
But it resulted in following error:
Error Code: 1305. PROCEDURE fn_relation_isModerator does not exist
Approach 2:
Query:
GRANT EXECUTE ON FUNCTION `db_name`.`fn_relation_isModerator` TO 'api_worker'#'%';
Error
Error Code: 1133. Can't find any matching row in the user table
Approach 3:
Query:
GRANT EXECUTE ON `db_name`.`fn_relation_isModerator` TO 'api_worker'#'%';
Error:
Error Code: 1144. Illegal GRANT/REVOKE command; please consult the
manual to see which privileges can be used
You have to use grant execute on the function (Approach 2):
GRANT EXECUTE ON FUNCTION `db_name`.`fn_relation_isModerator` TO 'api_workers'#'%';
As explained in the comments by OP, there was a typo in user, it should be api_workers instead of api_worker.
All approaches failed due to the use of non-existent user.

Allow MySQL users access to select on all views, but not tables

How do I allow users to have select access to Views only, and not any of the underlying tables?
I know how to grand permissions one at a time using a
grant select on database.viewName to 'User'
but I am trying to make either a user or a profile that have access to all views without running the grants 1 by 1 whenever a new view is created.
In Sequel Pro, under Users>Global Privileges and Schema Privileges I added "Show View" but this is not working.
This answer was verified using MySQL 8.0.23.
Make sure that your views are set to run using the credentials of the DEFINER, that way the invoking user doesn't require any TABLE privileges; not doing this will result in an error. For more details on the {DEFINER|INVOKER} parameter see here.
Create your user, allocating no privileges at all.
As root or another user with sufficient privileges, execute the following command:
GRANT SELECT ON <database>.<view_name> TO '<user>'#'<host>';
So if my database were cars and the view were called service_history, and I had a user john.doe that needed to access this from any host, I would execute the following:
GRANT SELECT ON cars.service_history TO 'john.doe'#'%';
Note how '%' was used to indicate any host is acceptable, you can replace this with an IP address, maybe a hostname (but I have not tested that).
I don't believe this is possible.
Mysql GRANT commands take the form GRANT permission ON object_type
The object_type clause, if present, should be specified as TABLE, FUNCTION, or PROCEDURE when the following object is a table, a stored function, or a stored procedure.
https://dev.mysql.com/doc/refman/5.7/en/grant.html
I don't believe there's any facility for granting permissions solely on a view.
Keep in mind that, in mysql, a view is just a query which is executed whenever you SELECT from it. It has no data storage or indexes of its own. As such, I expect you have to have access to the underlying tables in order for the view to be usable.
I will show it, might take a few edits to do it. Because I have to attach console output at the bottom.
Schema
create table users
( userId int auto_increment primary key,
userName varchar(100) not null
-- etc
);
insert users(userName) values ('Joe'),('Gertrude');
-- drop table secretdata;
create table SecretData
( id int auto_increment primary key,
userId int not null,
theKey varchar(100) not null,
theValue varchar(1000) not null,
key(userId),
CONSTRAINT fk_sd_users FOREIGN KEY (userId) REFERENCES users(userId)
);
insert secretdata(userId,theKey,theValue) values
(1,'FB Password','8*&Fjj_anchovieS'),
(1,'Proper response','I love the meal, just like I like it'),
(2,'thing7','goat');
The view
CREATE VIEW userSecrets AS
select u.userId,u.userName,s.theKey,s.theValue
from users u
join SecretData s
on s.userId=u.userId;
select * from userSecrets where userId=2; -- works here at the moment
Note my db name is so_gibberish.
The following is what you are asking about for grant rights to view:
GRANT SELECT ON so_gibberish.userSecrets TO 'john'#'host';
↑ ↑ users so provisioned can then rifle off queries against the view. ↑ ↑
however,
CREATE USER 'plebian2'#'localhost' IDENTIFIED BY 'mypass';
GRANT SELECT ON so_gibberish.userSecrets TO 'plebian2'#'localhost';
That user does have rights to use the view, but does not have access to the underlying tables directly.
Console output:
OS Prompt>mysql -u plebian2 -p
Enter password: ******
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 938
Server version: 5.6.24-log MySQL Community Server (GPL)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| so_gibberish |
+--------------------+
2 rows in set (0.00 sec)
mysql> use so_gibberish;
Database changed
mysql> show tables;
+------------------------+
| Tables_in_so_gibberish |
+------------------------+
| usersecrets |
+------------------------+
1 row in set (0.00 sec)
mysql> select * from users;
ERROR 1142 (42000): SELECT command denied to user 'plebian2'#'localhost' for table 'users'
mysql> select * from usersecrets;
+--------+----------+-----------------+--------------------------------------+
| userId | userName | theKey | theValue |
+--------+----------+-----------------+--------------------------------------+
| 1 | Joe | FB Password | 8*&Fjj_anchovieS |
| 1 | Joe | Proper response | I love the meal, just like I like it |
| 2 | Gertrude | thing7 | goat |
+--------+----------+-----------------+--------------------------------------+
3 rows in set (0.00 sec)
Lastly, I hope, see Manual Page entitled Access Control for Stored Programs and Views to see how the Definer permissions factor into the end-state effective rights. In particular, when the definer has less privileges than the user account using the view (a rather weird condition, but there it is).
Roles
In response to your comment under the Answer.
I was basically dissatisfied with the lack of roles embedded in mysql coming from the Microsoft world where they existed. Meaning, there are made-up roles, users are attached to roles, and grants are performed at the role level (in the MSFT world). There, in MSSQL, at the bottom of each create whatever block (stored proc etc), would be my block that did grants at the role level.
So coming into mysql, I had to create that role to user mapping. Good news, I only had to do it once. And the end of the chunk (if exist drop / create) that I run, it makes calls that directly manipulate the INFORMATION_SCHEMA.user_privileges and the like. But at a very high level, just like with MSFT, with the end result being what I wanted. That is, permissions granted.
That is a whole can of worms. But without it, good luck.
The commands like grant and some other commands can be performed on your own under the hood. There is little sacred about them. Yet they are there to protect novice programmers. And those that foresake those Warning Signs saying be careful are often left thinking,
"How do I get my data back. I know it's in there somewhere. Maybe I
shouldn't have done that under the hood thing I just did"
But definitely something to tinker with, especially in a throwaway db (one you don't care about). Even better, do it on a mysql instance you don't care about. Because we are talking about system wide implications at a higher level than at the db level.
I got this working by using user() as the filter on the tables and setting SQL SECURITY DEFINER, using separate schemas for views and source tables.
Example code
CREATE
ALGORITHM = UNDEFINED
DEFINER = `xxxxxx`#`%`
SQL SECURITY DEFINER
VIEW `XX`.`assets` AS
SELECT
`a`.`ID` AS `ID`,
`a`.`serial_no` AS `serial_no`,
`a`.`sys_id` AS `sys_id`,
`a`.`status` AS `status`,
`a`.`unit_name` AS `unit_name`,
`a`.`ip` AS `ip`,
`a`.`tzOffset` AS `tzOffset`
FROM
((`YY`.`Cust_Link_Table` `l`
JOIN `YY`.`systems` `s` ON ((`l`.`cust_id` = `s`.`cust_id`)))
JOIN `YY`.`assets` `a` ON ((`s`.`sys_id` = `a`.`sys_id`)))
WHERE
(`l`.`db_user` = CONVERT( LEFT(USER(), (LOCATE('#', USER()) - 1)) USING UTF8MB4))

Accessing mysql as root keeps logging me as anonymous user

When I try
sudo mysql -uroot -p
I get no errors, but it appears I'm not logged in as root, since:
SELECT USER(),CURRENT_USER();
returns:
+----------------+----------------+
| USER() | CURRENT_USER() |
+----------------+----------------+
| root#localhost | #localhost |
+----------------+----------------+
and I'm unable to perform any administrative commands like GRANT or UPDATE, I can only view information_schema table.
http://dev.mysql.com/doc/refman/4.1/en/information-functions.html#function_current-user says:
The value of CURRENT_USER() can differ from the value of
USER(). ... One way this might occur is that there is no
account listed in the grant tables for davida.
If you can't login as root, you can start MySQL without permissions checks: How to start MySQL with --skip-grant-tables? or start mysql with a init file http://dev.mysql.com/doc/refman/5.0/en/resetting-permissions.html. For the second option, you should use INSERT instead of UPDATE since your table doesn't have the root user.