[MDEV-20751] Permission Issue With Nested CTEs Created: 2019-10-04  Updated: 2020-12-18  Resolved: 2020-12-18

Status: Closed
Project: MariaDB Server
Component/s: Authentication and Privilege System, Optimizer - CTE
Affects Version/s: 10.4.8, 10.2, 10.3, 10.4
Fix Version/s: 10.2.37, 10.3.28, 10.4.18, 10.5.9

Type: Bug Priority: Major
Reporter: Kevin Andrews Assignee: Igor Babaev
Resolution: Fixed Votes: 1
Labels: None
Environment:

CentOS 7
Arch Linux



 Description   

Queries with CTEs referencing other CTEs eventually cause permission denied errors on users with SELECT permissions on specific list of tables. Issue is also present when using the newer roles based permissions.

Setups to reproduce:

-- Create Schema
CREATE DATABASE cte_permissions_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
 
-- Create Tables
CREATE TABLE cte_permissions_test.table1 (
	`id` INT NOT NULL AUTO_INCREMENT,
	`sku` varchar(100) NOT NULL,
	`type` varchar(100) NOT NULL,
	`active` TINYINT(1) NOT NULL DEFAULT 0,
	PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci;
 
CREATE TABLE cte_permissions_test.table2 (
	`id` INT NOT NULL AUTO_INCREMENT,
	`sku` varchar(100) NOT NULL,
	`type` TINYINT(1) NOT NULL,
	`active` TINYINT(1) NOT NULL DEFAULT 0,
	PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci;
 
 
CREATE TABLE cte_permissions_test.table3 (
	`id` INT NOT NULL AUTO_INCREMENT,
	`sku` varchar(100) NOT NULL,
	`qty` TINYINT(1) NOT NULL,
	PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci;
 
CREATE USER 'cte_restricted_user'@'127.0.0.1' IDENTIFIED BY '';
GRANT USAGE ON cte_permissions_test.* TO 'cte_restricted_user'@'127.0.0.1';
GRANT SELECT ON cte_permissions_test.table1 TO 'cte_restricted_user'@'127.0.0.1';
GRANT SELECT ON cte_permissions_test.table2 TO 'cte_restricted_user'@'127.0.0.1';
GRANT SELECT ON cte_permissions_test.table3 TO 'cte_restricted_user'@'127.0.0.1';
FLUSH PRIVILEGES;
 
mysql -u cte_restricted_user -h 127.0.0.1
USE cte_permissions_test;
 
WITH cte1 AS (
    SELECT * FROM table1 WHERE `type` IN ('type1', 'type2') AND active = 1
), cte2 AS (
    SELECT table2.* FROM table2
    INNER JOIN cte1 ON cte1.sku=table2.sku
    WHERE 
    	table2.`type` IN (1,2) AND 
    	table2.active = 1
), cte3 AS (
    SELECT table3.sku, cte1.`type`, table3.qty FROM table3
    INNER JOIN cte1 ON cte1.sku=table3.sku
    INNER JOIN cte2 ON table3.sku = cte2.sku
    WHERE
        table3.qty = 0
), cte4 AS (
    SELECT table3.* FROM table3
    INNER JOIN cte2 ON cte2.sku=table3.sku
    WHERE table3.sku IN (SELECT sku FROM cte1)
), cte5 AS (
    SELECT
        cte2.sku, cte2.`type`, cte2.active, cte3.sku as sku_holdings, cte3.qty  
    FROM cte2
    LEFT OUTER JOIN cte3 ON cte3.sku = cte2.sku
) SELECT * FROM cte5;
 
 
-- Result: ERROR 1142 (42000): SELECT command denied to user 'cte_restricted_user'@'localhost' for table 'cte1'
 
WITH cte1 AS (
    SELECT * FROM table1 WHERE `type` IN ('type1', 'type2') AND active = 1
) SELECT * FROM cte1;
 
-- Result Empty set (0.000 sec) - query runs succesfully and if results inserted does bring them back
 
 
-- Rewriting the above to use cte1 as a subquery where needed:
 
WITH cte1 AS (
    SELECT * FROM table1 WHERE `type` IN ('type1', 'type2') AND active = 1
), cte2 AS (
    SELECT table2.* FROM table2
    INNER JOIN (SELECT * FROM table1 WHERE `type` IN ('type1', 'type2') AND active = 1) AS cte1 ON cte1.sku=table2.sku
    WHERE 
    	table2.`type` IN (1,2) AND 
    	table2.active = 1
), cte3 AS (
    SELECT table3.sku, cte1.`type`, table3.qty FROM table3
    INNER JOIN (SELECT * FROM table1 WHERE `type` IN ('type1', 'type2') AND active = 1) AS cte1 ON cte1.sku=table3.sku
    INNER JOIN cte2 ON table3.sku = cte2.sku
    WHERE
        table3.qty = 0
), cte4 AS (
    SELECT table3.* FROM table3
    INNER JOIN cte2 ON cte2.sku=table3.sku
    WHERE table3.sku IN (SELECT sku FROM table1 WHERE `type` IN ('type1', 'type2') AND active = 1)
), cte5 AS (
    SELECT
        cte2.sku, cte2.`type`, cte2.active, cte3.sku as sku_holdings, cte3.qty  
    FROM cte2
    LEFT OUTER JOIN cte3 ON cte3.sku = cte2.sku
) SELECT * FROM cte5;
 
-- Result Empty set (0.001 sec) - query runs succesfully and if results inserted does bring them back 
 
SELECT VERSION();
 
-- Result 
-- +----------------+
-- | VERSION()      |
-- +----------------+
-- | 10.4.8-MariaDB |
-- +----------------+
-- 1 row in set (0.000 sec)

We've started relying on CTEs for anyone writing custom reports as an easy way to pick up and use existing query partials defined for our data sets. Significantly reduces the learning curve and makes reusing SQL code easier for our data team. However there is no way we can give users SELECT privilages on the entire schema, per table is a requirement here.

Happy to help further diagnose.



 Comments   
Comment by Alice Sherepa [ 2019-10-04 ]

Thanks for the report!
I repeated the same behavior on current MariaDB 10.2-10.4:

create database db;
use db;
 
CREATE TABLE t1 (id varchar(10));
insert into t1 values (1),(2),(3); 
 
CREATE USER 'u1'@'localhost';
GRANT USAGE ON db.* TO 'u1'@'localhost';
GRANT SELECT ON db.t1 TO 'u1'@'localhost';
FLUSH PRIVILEGES;
 
connect (u1,'localhost',u1,,);
connection u1;
use db;
 
WITH 
cte1 AS (SELECT 1 id FROM t1), 
cte2 AS (SELECT 1 id FROM t1 JOIN cte1 ON cte1.id =t1.id), 
cte3 AS (SELECT 1 id FROM t1 JOIN cte1 ON cte1.id =t1.id JOIN cte2 ON t1.id = cte2.id ), 
cte4 AS (SELECT 1    FROM t1 JOIN cte2 ON cte2.id =t1.id)
SELECT * FROM cte4;
# ERROR 1142: SELECT command denied to user 'u1'@'localhost' for table 'cte1'

Comment by Kevin Andrews [ 2019-10-04 ]

Not a problem, interesting to see it replicated with just one table... much better example!

Thanks for putting that together, mine was based off a live query which needed a lot of debugging and simplification to get down to what I reported. I'm glad it's replicated your side as well, makes things easier to track down.

Comment by Oleksandr Byelkin [ 2020-12-17 ]

OK to push after fixing what we have discussed

Comment by Igor Babaev [ 2020-12-18 ]

A fix for this bug was pushed into 10.2

Generated at Thu Feb 08 09:01:53 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.