Skip to content
This repository was archived by the owner on Sep 2, 2025. It is now read-only.

Commit 8b46b6c

Browse files
committed
drop the grants of authorized view when it's full refresh
1 parent 55689b9 commit 8b46b6c

File tree

5 files changed

+130
-10
lines changed

5 files changed

+130
-10
lines changed

dbt/adapters/bigquery/dataset.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,24 @@ def add_access_entry_to_dataset(dataset: Dataset, access_entry: AccessEntry) ->
4343
access_entries.append(access_entry)
4444
dataset.access_entries = access_entries
4545
return dataset
46+
47+
48+
49+
def delete_access_entry_to_dataset(dataset: Dataset, access_entry: AccessEntry) -> Dataset:
50+
"""Remove an access entry from a dataset, always use.
51+
52+
Args:
53+
dataset (Dataset): the dataset to be updated
54+
access_entry (AccessEntry): the access entry to be removed from the dataset
55+
"""
56+
access_entries = list(dataset.access_entries)
57+
access_entries_id = [entity.entity_id for entity in access_entries]
58+
59+
full_dataset_id = f"{dataset.project}.{dataset.dataset_id}"
60+
if access_entry.entity_id in access_entries_id:
61+
dataset.access_entries = [
62+
entry for entry in access_entries if entry.entity_id != access_entry.entity_id
63+
]
64+
else:
65+
print(f"no need to revoke the dataset access for '{access_entry.entity_id}' to ' dataset '{full_dataset_id}.' it doesn't exist")
66+
return dataset

dbt/adapters/bigquery/impl.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from dbt.adapters.bigquery import BigQueryColumn, BigQueryConnectionManager
4646
from dbt.adapters.bigquery.column import get_nested_column_data_types
4747
from dbt.adapters.bigquery.connections import BigQueryAdapterResponse
48-
from dbt.adapters.bigquery.dataset import add_access_entry_to_dataset, is_access_entry_in_dataset
48+
from dbt.adapters.bigquery.dataset import add_access_entry_to_dataset, is_access_entry_in_dataset, delete_access_entry_to_dataset
4949
from dbt.adapters.bigquery.python_submissions import (
5050
ClusterDataprocHelper,
5151
ServerlessDataProcHelper,
@@ -817,10 +817,12 @@ def describe_relation(
817817
return parser.from_bq_table(bq_table)
818818
return None
819819

820+
821+
820822
@available.parse_none
821-
def grant_access_to(self, entity, entity_type, role, grant_target_dict):
823+
def grant_access_to(self, entity, entity_type, role, grant_target_dict,full_refresh=False):
822824
"""
823-
Given an entity, grants it access to a dataset.
825+
Given an entity, grants access to a dataset.
824826
"""
825827
conn: BigQueryConnectionManager = self.connections.get_thread_connection()
826828
client = conn.handle
@@ -832,14 +834,54 @@ def grant_access_to(self, entity, entity_type, role, grant_target_dict):
832834
dataset_ref = self.connections.dataset_ref(grant_target.project, grant_target.dataset)
833835
dataset = client.get_dataset(dataset_ref)
834836
access_entry = AccessEntry(role, entity_type, entity)
835-
# only perform update if access entry not in dataset
837+
# only perform update if access entry in dataset but if full_refresh remove it first
836838
if is_access_entry_in_dataset(dataset, access_entry):
837-
logger.warning(f"Access entry {access_entry} " f"already exists in dataset")
838-
else:
839-
dataset = add_access_entry_to_dataset(dataset, access_entry)
840-
client.update_dataset(dataset, ["access_entries"])
839+
if not full_refresh:
840+
logger.warning(f"Access entry {access_entry} " f"already exists in dataset")
841+
return
842+
else:
843+
dataset = delete_access_entry_to_dataset(dataset,access_entry)
844+
dataset = client.update_dataset(
845+
dataset,
846+
["access_entries"],
847+
) # Make an API request.
848+
full_dataset_id = f"{dataset.project}.{dataset.dataset_id}"
849+
print(f"Revoked dataset access for '{access_entry.entity_id}' to ' dataset '{full_dataset_id}.'")
850+
dataset = add_access_entry_to_dataset(dataset, access_entry)
851+
dataset = client.update_dataset(dataset, ["access_entries"])
852+
full_dataset_id = f"{dataset.project}.{dataset.dataset_id}"
853+
print(f"allowed dataset access for '{access_entry.entity_id}' to ' dataset '{full_dataset_id}.'")
841854

842855
@available.parse_none
856+
def remove_grant_access_to(self, entity, entity_type, role, grant_target_dict):
857+
"""
858+
Given an entity, grants access to a dataset.
859+
"""
860+
conn: BigQueryConnectionManager = self.connections.get_thread_connection()
861+
client = conn.handle
862+
GrantTarget.validate(grant_target_dict)
863+
grant_target = GrantTarget.from_dict(grant_target_dict)
864+
if entity_type == "view":
865+
entity = self.get_table_ref_from_relation(entity).to_api_repr()
866+
with _dataset_lock:
867+
dataset_ref = self.connections.dataset_ref(grant_target.project, grant_target.dataset)
868+
dataset = client.get_dataset(dataset_ref)
869+
access_entry = AccessEntry(role, entity_type, entity)
870+
# only perform removing if access entry in dataset
871+
if is_access_entry_in_dataset(dataset, access_entry):
872+
dataset = delete_access_entry_to_dataset(dataset,access_entry)
873+
dataset = client.update_dataset(
874+
dataset,
875+
["access_entries"],
876+
) # Make an API request.
877+
878+
full_dataset_id = f"{dataset.project}.{dataset.dataset_id}"
879+
print(f"Revoked dataset access for '{access_entry.entity_id}' to ' dataset '{full_dataset_id}.'")
880+
else:
881+
logger.warning(f"Access entry {access_entry} not in the dataset {full_dataset_id} no need to remove it")
882+
@available.parse_none
883+
884+
843885
def get_dataset_location(self, relation):
844886
conn = self.connections.get_thread_connection()
845887
client = conn.handle

dbt/include/bigquery/macros/materializations/view.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
{% if config.get('grant_access_to') %}
2222
{% for grant_target_dict in config.get('grant_access_to') %}
23-
{% do adapter.grant_access_to(this, 'view', None, grant_target_dict) %}
23+
{% do adapter.grant_access_to(this, 'view', None, grant_target_dict,should_full_refresh()) %}
2424
{% endfor %}
2525
{% endif %}
2626

tests/unit/test_bigquery_adapter.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,35 @@ def test_grant_access_to_calls_update_with_valid_access_entry(self):
969969
self.mock_client.update_dataset.assert_called_once()
970970

971971

972+
def test_remove_grant_access_to_calls_update_with_valid_access_entry(self):
973+
a_different_entity = BigQueryRelation.from_dict(
974+
{
975+
"type": None,
976+
"path": {
977+
"database": "another-test-project",
978+
"schema": "test_schema_2",
979+
"identifier": "my_view",
980+
},
981+
"quote_policy": {"identifier": True},
982+
}
983+
)
984+
grant_target_dict = {"dataset": "someOtherDataset", "project": "someProject"}
985+
self.adapter.grant_access_to(
986+
entity=a_different_entity,
987+
entity_type="view",
988+
role=None,
989+
grant_target_dict=grant_target_dict,
990+
)
991+
self.mock_client.update_dataset.assert_called_once()
992+
self.adapter.remove_grant_access_to(
993+
entity=a_different_entity,
994+
entity_type="view",
995+
role=None,
996+
grant_target_dict=grant_target_dict,
997+
)
998+
self.mock_client.update_dataset.assert_called_once()
999+
1000+
9721001
@pytest.mark.parametrize(
9731002
["input", "output"],
9741003
[

tests/unit/test_dataset.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from dbt.adapters.bigquery.dataset import add_access_entry_to_dataset, is_access_entry_in_dataset
1+
from dbt.adapters.bigquery.dataset import add_access_entry_to_dataset, is_access_entry_in_dataset, delete_access_entry_to_dataset
22
from dbt.adapters.bigquery import BigQueryRelation
33

44
from google.cloud.bigquery import Dataset, AccessEntry, DatasetReference
@@ -88,3 +88,31 @@ def test_is_access_entry_in_dataset_returns_false_if_entry_not_in_dataset():
8888
dataset = Dataset(dataset_ref)
8989
access_entry = AccessEntry(None, "table", entity)
9090
assert not is_access_entry_in_dataset(dataset, access_entry)
91+
92+
93+
94+
95+
def test_delete_access_to_dataset_updates_dataset():
96+
"""
97+
test the removal of views grants to dataset
98+
"""
99+
database = "someDb"
100+
dataset = "someDataset"
101+
entity = BigQueryRelation.from_dict(
102+
{
103+
"type": None,
104+
"path": {
105+
"database": "test-project",
106+
"schema": "test_schema",
107+
"identifier": "my_table",
108+
},
109+
"quote_policy": {"identifier": False},
110+
}
111+
).to_dict()
112+
dataset_ref = DatasetReference(project=database, dataset_id=dataset)
113+
dataset = Dataset(dataset_ref)
114+
access_entry = AccessEntry(None, "table", entity)
115+
dataset = add_access_entry_to_dataset(dataset, access_entry)
116+
assert is_access_entry_in_dataset(dataset, access_entry)
117+
dataset = delete_access_entry_to_dataset(dataset, access_entry)
118+
assert not is_access_entry_in_dataset(dataset, access_entry)

0 commit comments

Comments
 (0)