Looking for a query to find all users who have a given permission (EG execute) for a given database.
Rationale: cleaning up ancient stored procedures and want to know who might be using them.
I can imagine a plug-n-chug SP where I loop through all the values returned from "show grants for xxx" but I'm hoping there is a better way.
select * from mysql.user where `Execute_priv` = 'Y'
Replace Execute_priv with the column name of the other priviledges you're after
For privileges on a DB by DB basis, try querying the mysql.db table:
select * from mysql.db where `Db` = 'databasename' and `Execute_priv` = 'Y'
Related
Currently I perform a manual two-step procedure to get the grants information for all the users.
Step 1:
SELECT user, host FROM mysql.user;
Step 2:
SHOW GRANTS FOR '«user»'#'«host»'; -- Repeated for all user-host pairs.
Is there a single command to give me this information?
You should be able to retrieve privileges for all users from information_schema:
select grantee, group_concat(privilege_type) from information_schema.user_privileges group by grantee
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.
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))
I use a user who has the update privileges to execute a sql:
update stu set age = 27 where name='zjw';
I got this error:
ERROR 1143 (42000): SELECT command denied to user
'update_user'#'localhost' for column 'name' in table 'stu'
The table like this:
CREATE TABLE `stu` (
`id` int(11) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)
update_user's privileges is:
grant update on *.* to 'update_user'#'%';
MySQL version is 5.1.73.
thanks.
When you execute
UPDATE `stu` SET age = 27 WHERE name = 'zjw';
the SQL engine has to first select the rows it needs to update.
Therefore, if you do not have the SELECT privilege, you cannot perform such update, even if you have the UPDATE privilege.
Check out the Manual for the Grant Syntax.
#Robin as per your last comment, try to understand your update statement-
UPDATE `stu` SET age = 27 WHERE name = 'zjw';
Your above update statement first try to fetch the records where name='zjw', so if your name column is indexed then select use index and select directly only records those have value 'zjw' else it will scan whole table and select 'zjw' wherever in table.
Means first mysql use select statement internally before update, so you also need select privileges with any other privileges like update/delete etc.
so your grant command should be-
GRANT SELECT, UPDATE on db.* to 'myuser'#'localhost' identified by 'mypass';
You should give permission only specific IP or localhost as per requirement instead of globally by '%' which is risky.
because when where clause is used in update, the datebase will select the rows that meet the where condition, so select privilege is needed while granting update privilege.
The SELECT privilege is also needed for other statements that read column values. For example, SELECT is needed for columns referenced on the right hand side of col_name=expr assignment in UPDATE statements or for columns named in the WHERE clause of DELETE or UPDATE statements.
mysql manual here
I have a mysql query that joins data across 2 databases and inserts new records into one of the databases. The query below works if the same mysql user has access to both databases and both are on the same server. However, how would you re-write if each database has different user credentials?
PHP Script snippet:
$hard = mysql_connect($hostname_hard, $username_hard, $password_hard)
or trigger_error(mysql_error(),E_USER_ERROR);
# Insert artists:
mysql_select_db($database_hard, $hard);
$query_artsync = "insert ignore into {$joomla_db}.jos_muscol_artists
(artist_name, letter, added,url,class_name)
select distinct
artist
, left(artist,1) as letter
, now()
, website, artist
from {$sam_db}.songlist
where (songtype='s' AND artist <>\"\")";
mysql_query($query_artsync, $hard) or die(mysql_error());
echo "<br>Artist tables merged! <br><br> Now merging albums<br><br>";
So..in the above the {$sam_db} database is accessed by a different user than the {$joomla_db} user...
There are more complex inserts following this one, but I think if I can get the above to work, then I'll likely be able to apply the same principles to the other insert queries...
you're talking about using 2 different connections in the same query, which, unfortunately, is not possible. what you'll have to do is get (select) all the information you need from the one database, and use that info to construct your insert query on the other database (2 separate queries).
Something like this:
$result = mysql_query("SELECT DISTINCT artist, LEFT(artist,1) AS letter, now() as added, website
FROM {$sam_db}.songlist
WHERE (songtype='s' AND artist <>\"\"", $sam_con);
while(($row = mysql_fetch_assoc($result)) != false)
{
$artist = mysql_real_escape_string($row['artist']);
$letter = mysql_real_escape_string($row['letter']);
$added = mysql_real_escape_string($row['added']);
$website = mysql_real_escape_string($row['website']);
mysql_query("INSERT IGNORE INTO {$joomla_db}.jos_muscol_artists
(artist_name, letter, added,url,class_name)
VALUES
('$artist', '$letter', '$added', '$website', '$artist')", $joomla_con);
}
where $sam_con and $joomla_con are your connection resources.
There is no problem querying tables from different databases.
SELECT a.*, b.*
FROM db1.table1 a
INNER JOIN db2.table2 b ON (a.id = b.id)
Will run with no problems, as will your insert query.
However the user that starts the query needs to have proper access to the databases and tables involved.
That means that user1 (who does the insert) has to be granted insert+select rights to table {$joomla_db}.jos_muscol_artists and select rights to {$sam_db}.songlist
If you don't want to expand the rights of your existing users, then you can just create a new inserter user that has the proper access rights to use both databases in the proper manner.
Only use this user to do inserts.
Alternative solution without adding users
Another option is to create a blackhole table on db1 (the db you select from)
CREATE TABLE db1.bh_insert_into_db2 (
f1 integer,
f2 varchar(255),
f3 varchar(255)
) ENGINE = BLACKHOLE;
And attach a trigger to that that does the insert into db2.
DELIMITER $$
CREATE TRIGGER ai_bh_insert_into_db2_each ON bh_insert_into_db2 FOR EACH ROW
BEGIN
INSERT INTO db2.table2 (f1,f2,f3) VALUES (NEW.f1, NEW.f2, NEW.f3);
END $$
DELIMITER ;
The insert into table db2.table2 will happen with the access rights of the user how created the trigger.
http://dev.mysql.com/doc/refman/5.1/en/grant.html
With PHP for example, you will have to create 2 unique mysql instances, 1 per connection.
Then use both and do individual queries.
Explanation...
Setup connections to both databases using mysql_connect for example. Now, you now have defined the connenction variables...
In the mysql_query you apply those variables, e.g mysql_query($query, $connect1) or mysql_query($query, $connect2).
From there you can extract and insert using code.