Skip to content

Commit c0ee97d

Browse files
authored
Added an integration test for table grants (#20150)
1 parent 39f6824 commit c0ee97d

File tree

5 files changed

+256
-0
lines changed

5 files changed

+256
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest_plugins = ['ydb.tests.library.fixtures', 'ydb.tests.library.flavours']
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# -*- coding: utf-8 -*-
2+
import logging
3+
from ydb.tests.oss.ydb_sdk_import import ydb
4+
5+
logger = logging.getLogger(__name__)
6+
7+
8+
# local configuration for the ydb cluster (fetched by ydb_cluster_configuration fixture)
9+
CLUSTER_CONFIG = dict(
10+
additional_log_configs={
11+
# 'TX_PROXY': LogLevels.DEBUG,
12+
}
13+
)
14+
DATABASE = "/Root/test"
15+
TABLE_NAME = "table"
16+
TABLE_PATH = f"{DATABASE}/{TABLE_NAME}"
17+
CREATE_TABLE_GRANTS = ['ydb.granular.create_table']
18+
ALTER_TABLE_ADD_COLUMN_GRANTS = ['ydb.granular.alter_schema', 'ydb.granular.describe_schema']
19+
ALTER_TABLE_DROP_COLUMN_GRANTS = ['ydb.granular.alter_schema', 'ydb.granular.describe_schema']
20+
DROP_TABLE_GRANTS = ['ydb.granular.remove_schema', 'ydb.granular.describe_schema']
21+
ALTER_TABLE_ADD_CHANGEFEED_GRANTS = ['ydb.granular.alter_schema', 'ydb.granular.describe_schema']
22+
ALTER_TOPIC_ADD_CONSUMER_GRANTS = ['ydb.granular.alter_schema', 'ydb.granular.describe_schema']
23+
READ_TOPIC_GRANTS = []
24+
ALTER_TABLE_DROP_CHANGEFEED_GRANTS = ['ydb.granular.alter_schema', 'ydb.granular.describe_schema']
25+
ALL_USED_GRANS = set(
26+
CREATE_TABLE_GRANTS
27+
+ ALTER_TABLE_ADD_COLUMN_GRANTS
28+
+ ALTER_TABLE_DROP_COLUMN_GRANTS
29+
+ DROP_TABLE_GRANTS
30+
+ ALTER_TABLE_ADD_CHANGEFEED_GRANTS
31+
+ ALTER_TOPIC_ADD_CONSUMER_GRANTS
32+
+ READ_TOPIC_GRANTS
33+
+ ALTER_TABLE_DROP_CHANGEFEED_GRANTS
34+
)
35+
36+
37+
def run_query(config, query):
38+
with ydb.Driver(config) as driver:
39+
with ydb.QuerySessionPool(driver, size=1) as pool:
40+
pool.execute_with_retries(query)
41+
42+
43+
def run_with_assert(config, query, expected_err=None):
44+
if not expected_err:
45+
run_query(config, query)
46+
return
47+
48+
try:
49+
run_query(config, query)
50+
assert False, 'Error expected'
51+
except Exception as e:
52+
assert expected_err in str(e)
53+
54+
55+
def create_user(ydb_cluster, admin_config, user_name):
56+
run_with_assert(admin_config, f"CREATE USER {user_name};")
57+
return ydb.DriverConfig(
58+
endpoint="%s:%s" % (ydb_cluster.nodes[1].host, ydb_cluster.nodes[1].port),
59+
database=DATABASE,
60+
credentials=ydb.StaticCredentials.from_user_password(user_name, ""),
61+
)
62+
63+
64+
def revoke_grants(admin_config, user_name, object_name, all_grants):
65+
if all_grants is None or len(all_grants) == 0:
66+
return
67+
68+
revoke_grants_query = ''
69+
for grant in all_grants:
70+
revoke_grants_query += f"REVOKE '{grant}' ON `{object_name}` FROM {user_name};"
71+
run_with_assert(admin_config, revoke_grants_query)
72+
73+
74+
def provide_grants(admin_config, user_name, object_name, required_grants):
75+
if required_grants is None or len(required_grants) == 0:
76+
return
77+
78+
provide_grants_query = ''
79+
for grant in required_grants:
80+
assert grant in ALL_USED_GRANS, 'Keep ALL_USED_GRANS updated with all used grants'
81+
provide_grants_query += f"GRANT '{grant}' ON `{object_name}` TO {user_name};"
82+
run_with_assert(admin_config, provide_grants_query)
83+
84+
85+
def _test_grants(admin_config, user_config, user_name, query, object_name, required_grants, expected_err):
86+
revoke_grants(admin_config, user_name, object_name, ALL_USED_GRANS)
87+
if required_grants is not None and len(required_grants) > 0: # means the query does not require any grants
88+
run_with_assert(user_config, query, expected_err=expected_err)
89+
provide_grants(admin_config, user_name, object_name, required_grants)
90+
run_with_assert(user_config, query)
91+
92+
93+
def test_granular_grants_for_tables(ydb_cluster):
94+
ydb_cluster.create_database(DATABASE, storage_pool_units_count={"hdd": 1})
95+
database_nodes = ydb_cluster.register_and_start_slots(DATABASE, count=1)
96+
ydb_cluster.wait_tenant_up(DATABASE)
97+
98+
tenant_admin_config = ydb.DriverConfig(
99+
endpoint="%s:%s" % (ydb_cluster.nodes[1].host, ydb_cluster.nodes[1].port),
100+
database=DATABASE,
101+
)
102+
103+
# CREATE TABLE
104+
user1_config = create_user(ydb_cluster, tenant_admin_config, "user1")
105+
create_table_query = f"CREATE TABLE {TABLE_NAME} (a Uint64, b Uint64, PRIMARY KEY (a));"
106+
_test_grants(
107+
tenant_admin_config,
108+
user1_config,
109+
'user1',
110+
create_table_query,
111+
DATABASE,
112+
CREATE_TABLE_GRANTS,
113+
"Access denied for scheme request",
114+
)
115+
116+
# ALTER TABLE ... ADD COLUMN
117+
user2_config = create_user(ydb_cluster, tenant_admin_config, "user2")
118+
add_column_query = f"ALTER TABLE `{TABLE_PATH}` ADD COLUMN `d` Uint64;"
119+
_test_grants(
120+
tenant_admin_config,
121+
user2_config,
122+
'user2',
123+
add_column_query,
124+
TABLE_PATH,
125+
ALTER_TABLE_ADD_COLUMN_GRANTS,
126+
"you do not have access permissions",
127+
)
128+
129+
# ALTER TABLE ... DROP COLUMN
130+
drop_column_query = f"ALTER TABLE `{TABLE_PATH}` DROP COLUMN `d`;"
131+
_test_grants(
132+
tenant_admin_config,
133+
user2_config,
134+
'user2',
135+
drop_column_query,
136+
TABLE_PATH,
137+
ALTER_TABLE_DROP_COLUMN_GRANTS,
138+
"you do not have access permissions",
139+
)
140+
141+
# DROP TABLE
142+
drop_table_query = f"DROP TABLE `{TABLE_PATH}`;"
143+
_test_grants(
144+
tenant_admin_config,
145+
user2_config,
146+
'user2',
147+
drop_table_query,
148+
TABLE_PATH,
149+
DROP_TABLE_GRANTS,
150+
"you do not have access permissions",
151+
)
152+
153+
ydb_cluster.remove_database(DATABASE)
154+
ydb_cluster.unregister_and_stop_slots(database_nodes)
155+
156+
157+
def test_cdc_grants(ydb_cluster):
158+
ydb_cluster.create_database(DATABASE, storage_pool_units_count={"hdd": 1})
159+
database_nodes = ydb_cluster.register_and_start_slots(DATABASE, count=1)
160+
ydb_cluster.wait_tenant_up(DATABASE)
161+
162+
tenant_admin_config = ydb.DriverConfig(
163+
endpoint="%s:%s" % (ydb_cluster.nodes[1].host, ydb_cluster.nodes[1].port),
164+
database=DATABASE,
165+
)
166+
167+
# setup table
168+
user1_config = create_user(ydb_cluster, tenant_admin_config, "user1")
169+
run_with_assert(tenant_admin_config, f"GRANT CREATE TABLE ON `{DATABASE}` TO user1;")
170+
run_with_assert(user1_config, f"CREATE TABLE {TABLE_NAME} (a Uint64, b Uint64, PRIMARY KEY (a));")
171+
run_with_assert(user1_config, f"INSERT INTO `{TABLE_PATH}` (a, b) VALUES (1, 1);")
172+
173+
# ALTER TABLE ... ADD CHANGEFEED
174+
user2_config = create_user(ydb_cluster, tenant_admin_config, "user2")
175+
create_changefeed_query = f"ALTER TABLE `{TABLE_PATH}` ADD CHANGEFEED updates WITH (FORMAT = 'JSON', MODE = 'NEW_AND_OLD_IMAGES', INITIAL_SCAN = TRUE);"
176+
_test_grants(
177+
tenant_admin_config,
178+
user2_config,
179+
'user2',
180+
create_changefeed_query,
181+
TABLE_PATH,
182+
ALTER_TABLE_ADD_CHANGEFEED_GRANTS,
183+
"you do not have access permissions",
184+
)
185+
186+
# ALTER TOPIC ... ADD CONSUMER
187+
user3_config = create_user(ydb_cluster, tenant_admin_config, "user3")
188+
add_consumer_query = f"ALTER TOPIC `{TABLE_PATH}/updates` ADD CONSUMER consumer;"
189+
_test_grants(
190+
tenant_admin_config,
191+
user3_config,
192+
'user3',
193+
add_consumer_query,
194+
TABLE_PATH,
195+
ALTER_TOPIC_ADD_CONSUMER_GRANTS,
196+
"you do not have access rights",
197+
)
198+
199+
# READ CHANGEFEED
200+
user4_config = create_user(ydb_cluster, tenant_admin_config, "user4")
201+
with ydb.Driver(user4_config) as driver:
202+
with driver.topic_client.reader(f"{TABLE_PATH}/updates", consumer="consumer", buffer_size_bytes=1000) as reader:
203+
message = reader.receive_message(timeout=5)
204+
assert '"newImage"' in message.data.decode("utf-8")
205+
206+
# ALTER TABLE ... DROP CHANGEFEED
207+
drop_changefeed_query = f"ALTER TABLE `{TABLE_PATH}` DROP CHANGEFEED updates;"
208+
_test_grants(
209+
tenant_admin_config,
210+
user4_config,
211+
'user4',
212+
drop_changefeed_query,
213+
TABLE_PATH,
214+
ALTER_TABLE_DROP_CHANGEFEED_GRANTS,
215+
"you do not have access permissions",
216+
)
217+
218+
ydb_cluster.remove_database(DATABASE)
219+
ydb_cluster.unregister_and_stop_slots(database_nodes)

ydb/tests/functional/security/ya.make

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
PY3TEST()
2+
3+
INCLUDE(${ARCADIA_ROOT}/ydb/tests/ydbd_dep.inc)
4+
5+
TEST_SRCS(
6+
conftest.py
7+
test_grants.py
8+
)
9+
10+
SPLIT_FACTOR(20)
11+
12+
INCLUDE(${ARCADIA_ROOT}/ydb/tests/library/flavours/flavours_deps.inc)
13+
14+
DEPENDS(
15+
)
16+
17+
PEERDIR(
18+
ydb/tests/library
19+
ydb/tests/library/fixtures
20+
ydb/tests/library/flavours
21+
ydb/tests/oss/ydb_sdk_import
22+
)
23+
24+
FORK_SUBTESTS()
25+
26+
IF (SANITIZER_TYPE)
27+
SIZE(LARGE)
28+
INCLUDE(${ARCADIA_ROOT}/ydb/tests/large.inc)
29+
REQUIREMENTS(ram:10 cpu:1)
30+
ELSE()
31+
SIZE(MEDIUM)
32+
ENDIF()
33+
34+
END()

ydb/tests/functional/ya.make

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ RECURSE(
2525
scheme_tests
2626
script_execution
2727
sdk/cpp/sdk_credprovider
28+
security
2829
serializable
2930
serverless
3031
sqs

0 commit comments

Comments
 (0)