我的主要目标是拥有一个数据库用户,我可以限制访问权限,以便他们看不到任何机密信息,同时仍然能够使用外键进行完整性检查。因此,必须允许下面的foo
用户只查看PUBLIC
项,同时仍然能够在其other_data
表上创建外键,以便该表不包含任何{{ 1}}值theyr无法看到。
这是设置:
id_item
-- These are items with a visibility
CREATE TABLE items (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
item_name VARCHAR(50) NOT NULL,
visibility ENUM('PUBLIC','PRIVATE','DELETED') NOT NULL DEFAULT 'PRIVATE',
INDEX (visibility),
UNIQUE INDEX (item_name)
)
ENGINE=INNODB;
INSERT INTO items (id, item_name, visibility)
VALUES (1, 'x', 'PUBLIC'), (2, 'y', 'PRIVATE'), (3, 'z', 'DELETED'), (4, 'xprime', 'PUBLIC'), (5, 'yprime', 'PRIVATE'), (6, 'zprime', 'DELETED');
-- This view only shows the public items (not private ones, not deleted ones)
CREATE VIEW public_items AS
(SELECT id, item_name FROM items WHERE visibility = 'PUBLIC');
-- How can I make this table definition only allow id_item to be a value from public_items.id?
-- I cannot use the view in the constraint, tho it would be perfect solution
CREATE TABLE other_data (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
label VARCHAR(50) NOT NULL,
id_item INT UNSIGNED NOT NULL,
UNIQUE KEY (label),
CONSTRAINT FOREIGN KEY (id_item) REFERENCES /*public_*/items (id)
ON UPDATE CASCADE ON DELETE RESTRICT
)ENGINE=INNODB;
-- Let's create the foo user that I want to restrict access rights
CREATE USER 'foo'@'localhost' IDENTIFIED BY 'bar';
GRANT USAGE ON *.* TO 'foo'@'localhost';
GRANT SELECT ON test_sql.public_items TO 'foo'@'localhost';
GRANT SELECT,INSERT ON test_sql.other_data TO 'foo'@'localhost';
FLUSH PRIVILEGES;
我在这里使用mysql 5.7,因此我可以在-- Now, use foo@localhost user
-- This should return (1;4) because foo must be allowed to see the public items => OK
SELECT * FROM public_items;
-- This should be DENIED because foo is not allowed to see all other items => OK
SELECT * FROM items;
-- This should be ALLOWED because foo is allowed to insert rows in other_data => OK
-- and this row refers to a public item
INSERT INTO other_data (id, label, id_item) VALUES (11, 'allowed-public', 1);
-- This should be ALLOWED because foo is allowed to see all the other_data => OK
SELECT * FROM other_data;
-- This should be FORBIDDEN because the item is not public => FAIL, it can be inserted...
INSERT INTO other_data (id, label, id_item) VALUES (12, 'forbidden-private', 2);
-- This should be FORBIDDEN because the item is not public => FAIL, it can be inserted
INSERT INTO other_data (id, label, id_item) VALUES (13, 'forbidden-deleted', 3);
-- This should be FORBIDDEN because the item does not exist => OK, it cannot be inserted
INSERT INTO other_data (id, label, id_item) VALUES (19, 'forbidden-notexist', 9);
表中使用一个生成的列,就像items
一样,将我的FK放在该列上,但是生成的列禁止引用自动-增量(id_if_public INT UNSIGNED GENERATED ALWAYS AS IF(visibility = 'PUBLIC', id, NULL) STORED
)列...
我可以使用id
之类的CHECK
语法移至MySQL 8,就像other_data
一样,但似乎CHECK EXISTS(SELECT 1 FROM public_items WHERE public_items.id = other_data.id_item)
不允许子查询...
我可以将CHECK
转换成表格并使用public_items
等对其进行“维护”,但是感觉很繁琐,并且需要TRIGGER
额外的存储空间,因此并非完全可扩展(即item_name
中的列越多,越重!)
答案 0 :(得分:0)
拥有这样一个新表public_items
并不是一个坏主意,因为您需要使用一些外键。您可以将其与视图结合使用,以从items
表中获取元信息,因此无需将它们两次添加到新的public_items
表中。这些表应如下所示:
CREATE TABLE items (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
item_name VARCHAR(50) NOT NULL,
visibility ENUM('PUBLIC','PRIVATE','DELETED') NOT NULL DEFAULT 'PRIVATE',
INDEX (visibility),
UNIQUE INDEX (item_name),
UNIQUE INDEX (id, visibility) /* new index over 'id' and 'visibility' */
);
现在您创建一个新表public_items
,其外键位于(id, visibility)
上,但是强制只使用"PUBLIC"
个条目。
CREATE TABLE public_items (
id INT UNSIGNED NOT NULL PRIMARY KEY,
visibility ENUM('PUBLIC') NOT NULL, /* only allow public items */
CONSTRAINT FK_public_items_id_visibility FOREIGN KEY (id, visibility)
REFERENCES items(id, visibility)
);
现在,您只能在items
的{{1}}中添加公开的行,并且可以通过外键引用这些行。您的public_items
表必须将外键更改为此新的other_data
表。
显然,这种方法有一些缺点:
public_items
中的条目来维护public_items
中的公共项目列表。您可以使用TRIGGER或后台进程来使这些条目保持同步,也可以使用其他任何方法。items
表中没有public_items
列。如果您不想将这些元信息复制到item_name
表中(提到了同步/更新过程),可能仍需要一个视图来从items
表中获取公共项目及其元信息。以上)。