Skip to content

Commit 2c6c6a6

Browse files
committed
remove _apply_grants_config _revoke_grants_config API from engine adapter
and make sync_grants_config public
1 parent 9af8b2b commit 2c6c6a6

File tree

6 files changed

+18
-257
lines changed

6 files changed

+18
-257
lines changed

sqlmesh/core/engine_adapter/base.py

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2419,7 +2419,7 @@ def _get_current_grants_config(self, table: exp.Table) -> GrantsConfig:
24192419
raise NotImplementedError(f"Engine does not support grants: {type(self)}")
24202420
raise NotImplementedError("Subclass must implement get_current_grants")
24212421

2422-
def _sync_grants_config(
2422+
def sync_grants_config(
24232423
self,
24242424
table: exp.Table,
24252425
grants_config: GrantsConfig,
@@ -2446,45 +2446,6 @@ def _sync_grants_config(
24462446
if dcl_exprs:
24472447
self.execute(dcl_exprs)
24482448

2449-
def _apply_grants_config(
2450-
self,
2451-
table: exp.Table,
2452-
grants_config: GrantsConfig,
2453-
table_type: DataObjectType = DataObjectType.TABLE,
2454-
) -> None:
2455-
"""Applies grants to a table.
2456-
2457-
Args:
2458-
table: The table/view to grant permissions on.
2459-
grants_config: Dictionary mapping privileges to lists of grantees.
2460-
table_type: The type of database object (TABLE, VIEW, MATERIALIZED_VIEW).
2461-
2462-
Raises:
2463-
NotImplementedError: If the engine does not support grants.
2464-
"""
2465-
2466-
if grants := self._apply_grants_config_expr(table, grants_config, table_type):
2467-
self.execute(grants)
2468-
2469-
def _revoke_grants_config(
2470-
self,
2471-
table: exp.Table,
2472-
grants_config: GrantsConfig,
2473-
table_type: DataObjectType = DataObjectType.TABLE,
2474-
) -> None:
2475-
"""Revokes grants from a table.
2476-
2477-
Args:
2478-
table: The table/view to revoke privileges from.
2479-
grants_config: Dictionary mapping privileges to lists of grantees.
2480-
table_type: The type of database object (TABLE, VIEW, MATERIALIZED_VIEW).
2481-
2482-
Raises:
2483-
NotImplementedError: If the engine does not support grants.
2484-
"""
2485-
if revokes := self._revoke_grants_config_expr(table, grants_config, table_type):
2486-
self.execute(revokes)
2487-
24882449
def _apply_grants_config_expr(
24892450
self,
24902451
table: exp.Table,

sqlmesh/core/snapshot/evaluator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,7 +1699,7 @@ def _apply_grants(
16991699
logger.info(f"Applying grants for model {model.name} to table {table_name}")
17001700

17011701
try:
1702-
self.adapter._sync_grants_config(
1702+
self.adapter.sync_grants_config(
17031703
exp.to_table(table_name, dialect=model.dialect),
17041704
grants_config,
17051705
model.grants_table_type,

tests/core/engine_adapter/integration/test_integration_postgres.py

Lines changed: 6 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -374,159 +374,6 @@ def _mutate_config(gateway: str, config: Config):
374374
# Grants Integration Tests
375375

376376

377-
def test_grants_apply_on_table(
378-
engine_adapter: PostgresEngineAdapter, ctx: TestContext, config: Config
379-
):
380-
with create_users(engine_adapter, "reader", "writer", "admin") as roles:
381-
table = ctx.table("grants_test_table")
382-
engine_adapter.create_table(
383-
table, {"id": exp.DataType.build("INT"), "name": exp.DataType.build("VARCHAR(50)")}
384-
)
385-
386-
engine_adapter.execute(f"INSERT INTO {table} VALUES (1, 'test')")
387-
388-
grants_config = {
389-
"SELECT": [roles["reader"]["username"], roles["admin"]["username"]],
390-
"INSERT": [roles["writer"]["username"], roles["admin"]["username"]],
391-
"DELETE": [roles["admin"]["username"]],
392-
}
393-
394-
engine_adapter._apply_grants_config(table, grants_config)
395-
396-
schema_name = table.db
397-
for role_data in roles.values():
398-
engine_adapter.execute(
399-
f'GRANT USAGE ON SCHEMA "{schema_name}" TO "{role_data["username"]}"'
400-
)
401-
402-
current_grants = engine_adapter._get_current_grants_config(table)
403-
404-
assert "SELECT" in current_grants
405-
assert roles["reader"]["username"] in current_grants["SELECT"]
406-
assert roles["admin"]["username"] in current_grants["SELECT"]
407-
408-
assert "INSERT" in current_grants
409-
assert roles["writer"]["username"] in current_grants["INSERT"]
410-
assert roles["admin"]["username"] in current_grants["INSERT"]
411-
412-
assert "DELETE" in current_grants
413-
assert roles["admin"]["username"] in current_grants["DELETE"]
414-
415-
# Reader should be able to SELECT but not INSERT
416-
with engine_adapter_for_role(roles["reader"], ctx, config) as reader_adapter:
417-
result = reader_adapter.fetchone(f"SELECT COUNT(*) FROM {table}")
418-
assert result == (1,), "Reader should be able to SELECT from table"
419-
420-
with engine_adapter_for_role(roles["reader"], ctx, config) as reader_adapter:
421-
with pytest.raises(Exception):
422-
reader_adapter.execute(f"INSERT INTO {table} VALUES (2, 'test2')")
423-
424-
# Writer should be able to INSERT but not SELECT
425-
with engine_adapter_for_role(roles["writer"], ctx, config) as writer_adapter:
426-
writer_adapter.execute(f"INSERT INTO {table} VALUES (3, 'test3')")
427-
428-
with engine_adapter_for_role(roles["writer"], ctx, config) as writer_adapter:
429-
with pytest.raises(Exception):
430-
writer_adapter.fetchone(f"SELECT COUNT(*) FROM {table}")
431-
432-
# Admin should be able to SELECT, INSERT, and DELETE
433-
with engine_adapter_for_role(roles["admin"], ctx, config) as admin_adapter:
434-
result = admin_adapter.fetchone(f"SELECT COUNT(*) FROM {table}")
435-
assert result == (2,), "Admin should be able to SELECT from table"
436-
437-
admin_adapter.execute(f"INSERT INTO {table} VALUES (4, 'test4')")
438-
admin_adapter.execute(f"DELETE FROM {table} WHERE id = 4")
439-
440-
441-
def test_grants_apply_on_view(
442-
engine_adapter: PostgresEngineAdapter, ctx: TestContext, config: Config
443-
):
444-
with create_users(engine_adapter, "reader", "admin") as roles:
445-
base_table = ctx.table("grants_base_table")
446-
engine_adapter.create_table(
447-
base_table,
448-
{"id": exp.DataType.build("INT"), "value": exp.DataType.build("VARCHAR(50)")},
449-
)
450-
451-
view_table = ctx.table("grants_test_view")
452-
engine_adapter.create_view(view_table, exp.select().from_(base_table))
453-
454-
# Grant schema access for authentication tests
455-
test_schema = view_table.db
456-
for role_credentials in roles.values():
457-
engine_adapter.execute(
458-
f'GRANT USAGE ON SCHEMA "{test_schema}" TO "{role_credentials["username"]}"'
459-
)
460-
461-
grants_config = {"SELECT": [roles["reader"]["username"], roles["admin"]["username"]]}
462-
463-
engine_adapter._apply_grants_config(view_table, grants_config)
464-
465-
current_grants = engine_adapter._get_current_grants_config(view_table)
466-
assert "SELECT" in current_grants
467-
assert roles["reader"]["username"] in current_grants["SELECT"]
468-
assert roles["admin"]["username"] in current_grants["SELECT"]
469-
470-
# Test actual authentication - reader should be able to SELECT from view
471-
with engine_adapter_for_role(roles["reader"], ctx, config) as reader_adapter:
472-
reader_adapter.fetchone(f"SELECT COUNT(*) FROM {view_table}")
473-
474-
475-
def test_grants_revoke(engine_adapter: PostgresEngineAdapter, ctx: TestContext, config: Config):
476-
with create_users(engine_adapter, "reader", "writer") as roles:
477-
table = ctx.table("grants_revoke_test")
478-
engine_adapter.create_table(table, {"id": exp.DataType.build("INT")})
479-
engine_adapter.execute(f"INSERT INTO {table} VALUES (1)")
480-
481-
# Grant schema access for authentication tests
482-
test_schema = table.db
483-
for role_credentials in roles.values():
484-
engine_adapter.execute(
485-
f'GRANT USAGE ON SCHEMA "{test_schema}" TO "{role_credentials["username"]}"'
486-
)
487-
488-
initial_grants = {
489-
"SELECT": [roles["reader"]["username"], roles["writer"]["username"]],
490-
"INSERT": [roles["writer"]["username"]],
491-
}
492-
engine_adapter._apply_grants_config(table, initial_grants)
493-
494-
initial_current_grants = engine_adapter._get_current_grants_config(table)
495-
assert roles["reader"]["username"] in initial_current_grants.get("SELECT", [])
496-
assert roles["writer"]["username"] in initial_current_grants.get("SELECT", [])
497-
assert roles["writer"]["username"] in initial_current_grants.get("INSERT", [])
498-
499-
# Verify reader can SELECT before revoke
500-
with engine_adapter_for_role(roles["reader"], ctx, config) as reader_adapter:
501-
reader_adapter.fetchone(f"SELECT COUNT(*) FROM {table}")
502-
503-
revoke_grants = {
504-
"SELECT": [roles["reader"]["username"]],
505-
"INSERT": [roles["writer"]["username"]],
506-
}
507-
engine_adapter._revoke_grants_config(table, revoke_grants)
508-
509-
current_grants_after = engine_adapter._get_current_grants_config(table)
510-
511-
assert roles["reader"]["username"] not in current_grants_after.get("SELECT", [])
512-
assert roles["writer"]["username"] in current_grants_after.get("SELECT", [])
513-
assert roles["writer"]["username"] not in current_grants_after.get("INSERT", [])
514-
515-
# Verify reader can NO LONGER SELECT after revoke
516-
with engine_adapter_for_role(roles["reader"], ctx, config) as reader_adapter:
517-
with pytest.raises(Exception):
518-
reader_adapter.fetchone(f"SELECT COUNT(*) FROM {table}")
519-
520-
# Verify writer can still SELECT but not INSERT after revoke
521-
with engine_adapter_for_role(roles["writer"], ctx, config) as writer_adapter:
522-
result = writer_adapter.fetchone(f"SELECT COUNT(*) FROM {table}")
523-
assert result is not None
524-
assert result[0] == 1
525-
with engine_adapter_for_role(roles["writer"], ctx, config) as writer_adapter:
526-
with pytest.raises(Exception):
527-
writer_adapter.execute(f"INSERT INTO {table} VALUES (2)")
528-
529-
530377
def test_grants_sync(engine_adapter: PostgresEngineAdapter, ctx: TestContext, config: Config):
531378
with create_users(engine_adapter, "user1", "user2", "user3") as roles:
532379
table = ctx.table("grants_sync_test")
@@ -538,7 +385,7 @@ def test_grants_sync(engine_adapter: PostgresEngineAdapter, ctx: TestContext, co
538385
"SELECT": [roles["user1"]["username"], roles["user2"]["username"]],
539386
"INSERT": [roles["user1"]["username"]],
540387
}
541-
engine_adapter._apply_grants_config(table, initial_grants)
388+
engine_adapter.sync_grants_config(table, initial_grants)
542389

543390
initial_current_grants = engine_adapter._get_current_grants_config(table)
544391
assert roles["user1"]["username"] in initial_current_grants.get("SELECT", [])
@@ -549,7 +396,7 @@ def test_grants_sync(engine_adapter: PostgresEngineAdapter, ctx: TestContext, co
549396
"SELECT": [roles["user2"]["username"], roles["user3"]["username"]],
550397
"UPDATE": [roles["user3"]["username"]],
551398
}
552-
engine_adapter._sync_grants_config(table, target_grants)
399+
engine_adapter.sync_grants_config(table, target_grants)
553400

554401
final_grants = engine_adapter._get_current_grants_config(table)
555402

@@ -572,13 +419,13 @@ def test_grants_sync_empty_config(
572419
"SELECT": [roles["user"]["username"]],
573420
"INSERT": [roles["user"]["username"]],
574421
}
575-
engine_adapter._apply_grants_config(table, initial_grants)
422+
engine_adapter.sync_grants_config(table, initial_grants)
576423

577424
initial_current_grants = engine_adapter._get_current_grants_config(table)
578425
assert roles["user"]["username"] in initial_current_grants.get("SELECT", [])
579426
assert roles["user"]["username"] in initial_current_grants.get("INSERT", [])
580427

581-
engine_adapter._sync_grants_config(table, {})
428+
engine_adapter.sync_grants_config(table, {})
582429

583430
final_grants = engine_adapter._get_current_grants_config(table)
584431
assert final_grants == {}
@@ -601,7 +448,7 @@ def test_grants_case_insensitive_grantees(
601448
writer = roles["test_writer"]["username"]
602449

603450
grants_config = {"SELECT": [reader, writer.upper()]}
604-
engine_adapter._apply_grants_config(table, grants_config)
451+
engine_adapter.sync_grants_config(table, grants_config)
605452

606453
# Grantees are still in lowercase
607454
current_grants = engine_adapter._get_current_grants_config(table)
@@ -610,7 +457,7 @@ def test_grants_case_insensitive_grantees(
610457

611458
# Revoke writer
612459
grants_config = {"SELECT": [reader.upper()]}
613-
engine_adapter._sync_grants_config(table, grants_config)
460+
engine_adapter.sync_grants_config(table, grants_config)
614461

615462
current_grants = engine_adapter._get_current_grants_config(table)
616463
assert reader in current_grants.get("SELECT", [])

tests/core/engine_adapter/test_base.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,25 +3791,7 @@ def test_sync_grants_config_unsupported_engine(make_mocked_engine_adapter: t.Cal
37913791
grants_config = {"SELECT": ["user1"]}
37923792

37933793
with pytest.raises(NotImplementedError, match="Engine does not support grants"):
3794-
adapter._sync_grants_config(relation, grants_config)
3795-
3796-
3797-
def test_apply_grants_config_expr_not_implemented(make_mocked_engine_adapter: t.Callable):
3798-
adapter = make_mocked_engine_adapter(EngineAdapter)
3799-
relation = exp.to_table("test_table")
3800-
grants_config = {"SELECT": ["user1"]}
3801-
3802-
with pytest.raises(NotImplementedError):
3803-
adapter._apply_grants_config_expr(relation, grants_config)
3804-
3805-
3806-
def test_revoke_grants_config_expr_not_implemented(make_mocked_engine_adapter: t.Callable):
3807-
adapter = make_mocked_engine_adapter(EngineAdapter)
3808-
relation = exp.to_table("test_table")
3809-
grants_config = {"SELECT": ["user1"]}
3810-
3811-
with pytest.raises(NotImplementedError):
3812-
adapter._revoke_grants_config_expr(relation, grants_config)
3794+
adapter.sync_grants_config(relation, grants_config)
38133795

38143796

38153797
def test_get_current_grants_config_not_implemented(make_mocked_engine_adapter: t.Callable):

tests/core/engine_adapter/test_postgres.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from sqlglot.helper import ensure_list
88

99
from sqlmesh.core.engine_adapter import PostgresEngineAdapter
10-
from sqlmesh.core.engine_adapter.shared import DataObjectType
1110
from sqlmesh.utils.errors import SQLMeshError
1211
from tests.core.engine_adapter import to_sql_calls
1312

@@ -180,34 +179,6 @@ def test_server_version(make_mocked_engine_adapter: t.Callable, mocker: MockerFi
180179
assert adapter.server_version == (15, 13)
181180

182181

183-
def test_apply_grants_config(make_mocked_engine_adapter: t.Callable):
184-
adapter = make_mocked_engine_adapter(PostgresEngineAdapter)
185-
relation = exp.to_table("test_table", dialect="postgres")
186-
grants_config = {"SELECT": ["user1", "user2"], "INSERT": ["admin_user"]}
187-
188-
adapter._apply_grants_config(relation, grants_config, DataObjectType.TABLE)
189-
190-
sql_calls = to_sql_calls(adapter)
191-
192-
assert len(sql_calls) == 2
193-
assert 'GRANT SELECT ON "test_table" TO user1, user2' in sql_calls
194-
assert 'GRANT INSERT ON "test_table" TO admin_user' in sql_calls
195-
196-
197-
def test_revoke_grants_config(make_mocked_engine_adapter: t.Callable):
198-
adapter = make_mocked_engine_adapter(PostgresEngineAdapter)
199-
relation = exp.to_table("test_table", dialect="postgres")
200-
grants_config = {"SELECT": ["old_user"], "INSERT": ["removed_role"]}
201-
202-
adapter._revoke_grants_config(relation, grants_config, DataObjectType.TABLE)
203-
204-
sql_calls = to_sql_calls(adapter)
205-
206-
assert len(sql_calls) == 2
207-
assert 'REVOKE SELECT ON "test_table" FROM old_user' in sql_calls
208-
assert 'REVOKE INSERT ON "test_table" FROM removed_role' in sql_calls
209-
210-
211182
def test_sync_grants_config(make_mocked_engine_adapter: t.Callable, mocker: MockerFixture):
212183
adapter = make_mocked_engine_adapter(PostgresEngineAdapter)
213184
relation = exp.to_table("test_schema.test_table", dialect="postgres")
@@ -216,7 +187,7 @@ def test_sync_grants_config(make_mocked_engine_adapter: t.Callable, mocker: Mock
216187
current_grants = [("SELECT", "old_user"), ("UPDATE", "admin_user")]
217188
fetchall_mock = mocker.patch.object(adapter, "fetchall", return_value=current_grants)
218189

219-
adapter._sync_grants_config(relation, new_grants_config)
190+
adapter.sync_grants_config(relation, new_grants_config)
220191

221192
fetchall_mock.assert_called_once()
222193
executed_query = fetchall_mock.call_args[0][0]
@@ -252,7 +223,7 @@ def test_sync_grants_config_with_overlaps(
252223
]
253224
fetchall_mock = mocker.patch.object(adapter, "fetchall", return_value=current_grants)
254225

255-
adapter._sync_grants_config(relation, new_grants_config)
226+
adapter.sync_grants_config(relation, new_grants_config)
256227

257228
fetchall_mock.assert_called_once()
258229
executed_query = fetchall_mock.call_args[0][0]
@@ -298,7 +269,7 @@ def test_sync_grants_config_with_default_schema(
298269
fetchall_mock = mocker.patch.object(adapter, "fetchall", return_value=currrent_grants)
299270
get_schema_mock = mocker.patch.object(adapter, "get_current_schema", return_value="public")
300271

301-
adapter._sync_grants_config(relation, new_grants_config)
272+
adapter.sync_grants_config(relation, new_grants_config)
302273

303274
get_schema_mock.assert_called_once()
304275

0 commit comments

Comments
 (0)