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))
Related
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.
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.
I'm trying to use Mariadb columnstore, but I'm having some errors when inserting data in a table with a autoincrement column defined.
The issue happens when using the JDBC driver.
CREATE TABLE schema.mytable
(
deaf_id bigint NOT NULL COMMENT 'autoincrement=1',
name varchar(80) NOT NULL,
country varchar(14) NOT NULL
) ENGINE=ColumnStore;
Using DBeaver, I try to insert a new data on the table:
INSERT INTO schema.mytable
(name, country)
VALUES('ny', 'usa');
But I get the following error:
SQL Error [1364] [HY000]: (conn:4) Field 'deaf_id' doesn't have a
default value
But using the mcsmysql, I can add the data successfully:
MariaDB [schema]> INSERT INTO schema.mytable (name, country) VALUES('ny', 'usa');
Query OK, 1 row affected, 1 warning (0.28 sec)
MariaDB [schema]> select * from schema.mytable;
+---------+------+---------+
| deaf_id | name | country |
+---------+------+---------+
| 1 | ny | usa |
+---------+------+---------+
1 row in set (0.07 sec)
MariaDB [bovespa]>
I tried to use the both drivers:
https://downloads.mariadb.com/Connectors/java/connector-java-2.0.1/mariadb-java-client-2.0.1.jar
https://downloads.mariadb.com/Connectors/java/connector-java-1.5.9/mariadb-java-client-1.5.9.jar
Is there any known issue for this ? Or am I missing something ?
Kleyson Rios.
Short answer:
Is not a client or driver issue. It is a Columnstore user privileges issue.
Suppose that your user that you use to connect to Columnstore is called 'myuser'. Run the following as root:
-- if necessary replace % below with localhost or whatever IP you use for the connection
GRANT ALL PRIVILEGES ON `infinidb_vtable`.* TO 'myuser'#'%';
FLUSH PRIVILEGES;
Now try to connect again with 'myuser', no matter whether you use DBeaver or whatever client. You should be able to run the insert.
Long answer:
First of all, let's review the proper create table syntax because they are different between Columnstore and Innodb when it comes to defining the autoincrement fields:
-- Columnstore syntax
CREATE TABLE schema.mytable
(
deaf_id bigint NOT NULL COMMENT 'autoincrement=1',
name varchar(80) NOT NULL,
country varchar(14) NOT NULL
) ENGINE=ColumnStore;
-- InnoDB syntax
CREATE TABLE schema.mytable
(
deaf_id bigint NOT NULL AUTO_INCREMENT,
name varchar(80) NOT NULL,
country varchar(14) NOT NULL,
PRIMARY KEY (`deaf_id`)
) ENGINE=InnoDB;
Don't mix them up as you might get errors or unexpected results.
The issue you described gave me some headaches too in the past. The error below is pretty misleading:
SQL Error [1364] [HY000]: (conn:4) Field 'deaf_id' doesn't have a
default value
If you tried to run:
select * from mytable;
you would get a much more useful error:
ERROR 1044 (42000): Access denied for user 'myuser'#'%' to database 'infinidb_vtable'
Based on this error I found this page which gives detailed explanations:
https://mariadb.com/kb/en/mariadb/columnstore-database-user-management/
So, just run the GRANT that I wrote at the beginning of the answer and your problem is solved. Don't forget to run the same grant on all the users that you want to be able to access Columnstore tables.
As a short advice before finishing..
Think twice whether you want to use Columnstore or stick with MySQL/MariaDB InnoDB.
We are currently under a Columnstore evaluation. It showed us some really impressive performance when it came to the speed of selects on huge tables. I doubt I could have obtained half of that performance with MySQL/MariaDB even after ellaborate tuning.
But Columnstore has its own drawbacks. I would mention two of them which gave us some headaches:
the speed of the inserts is poor (up to 20 times slower than on a simillar InnoDB table according to some of our tests)
Columnstore did not yet reach maturity so it might give you headaches at times
So choose your database engine carefully.
[Edit:]
I forgot to mention that I'm speaking about MariaDB Columnstore 1.0.9. Future versions might show different syntax/behaviour, etc..
GRANT PRIVILEGES didn't solve my problem. I fixed this by declaring the id autoincrement field as NULL instead of NOT NULL.
CREATE TABLE schema.mytable
(
id bigint NULL COMMENT 'autoincrement=1',
name varchar(80) NOT NULL,
country varchar(14) NOT NULL
) ENGINE=ColumnStore;
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.
I'm facing a problem with user access control. To be more clear, the mysql user I create has access to other tables than what I've given access to.
Let's say I log in as root into mysql and there are many databases, and in database "test", there are many tables one of which is "news".
I want to create user specifically having access only to "select" values from "test.news". This is how it looked:
mysql> grant select on test.news to 'new_user2'#'localhost' identified by 'XXXXXXX';
Query OK, 0 rows affected (0.00 sec)
Flushed privileges: flush privileges;
Now, log out, and I login as new_user2:
/opt/lampp/bin/./mysql -u new_user2 -p
and want to see databases there:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| test |
+--------------------+
2 rows in set (0.00 sec)
This was good, in the sense, there were other databases in the "root" account which were not visible to new_user2. Then, I wanted to see tables in test. So-
use test;
show tables;
This is the first shock for me..
I saw all the tables in the "test" database, which I was not expecting. All I wanted to see was "news" table. Or is it normal that all users who have been given access to one table in a database, can actually view all the tables?
second shock, I used select * for another table- "user", which is essentially another table in "test", and I could see all the contents in user table.
So, when I created "new_user2" and granted permission only to access test.news, how can it access test.user? select * from news is working fine..
Did I really restrict access to all other tables in "test" database? If not, how do I do it?
OK. As if this is not sufficient, I can actually delete tables created by 'root'..
mysql> drop table member;
Query OK, 0 rows affected (0.42 sec)
These are the grants for 'new_user2'#'localhost' when logged in root...
mysql> SHOW GRANTS FOR 'new_user2'#'localhost';
+------------------------------------------------------------------------------------------------------------------+
| Grants for new_user2#localhost |
+------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'new_user2'#'localhost' IDENTIFIED BY PASSWORD '*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' |
| GRANT SELECT ON `test`.`news` TO 'new_user2'#'localhost' |
+------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
I feel, I'm missing something basic here. Please help me this. Thank you so much.