Skip to content

Commit 399af09

Browse files
author
Lihan Li
committed
Added service account impersonation feature
1 parent 45fcad4 commit 399af09

File tree

4 files changed

+21
-0
lines changed

4 files changed

+21
-0
lines changed

sqlalchemy_bigquery/_helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def create_bigquery_client(
3636
default_query_job_config=None,
3737
location=None,
3838
project_id=None,
39+
with_subject=None,
3940
):
4041
default_project = None
4142

@@ -57,6 +58,9 @@ def create_bigquery_client(
5758
else:
5859
credentials, default_project = google.auth.default(scopes=SCOPES)
5960

61+
if with_subject:
62+
credentials = credentials.with_subject(with_subject)
63+
6064
if project_id is None:
6165
project_id = default_project
6266

sqlalchemy_bigquery/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ def create_connect_args(self, url):
821821
credentials_base64,
822822
default_query_job_config,
823823
list_tables_page_size,
824+
with_subject,
824825
user_supplied_client,
825826
) = parse_url(url)
826827

@@ -846,6 +847,7 @@ def create_connect_args(self, url):
846847
project_id=project_id,
847848
location=self.location,
848849
default_query_job_config=default_query_job_config,
850+
with_subject=with_subject,
849851
)
850852
return ([], {"client": client})
851853

sqlalchemy_bigquery/parse_url.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def parse_url(url): # noqa: C901
7171
credentials_base64 = None
7272
list_tables_page_size = None
7373
user_supplied_client = False
74+
with_subject = None
7475

7576
# location
7677
if "location" in query:
@@ -106,6 +107,10 @@ def parse_url(url): # noqa: C901
106107
if "user_supplied_client" in query:
107108
user_supplied_client = query.pop("user_supplied_client").lower() == "true"
108109

110+
# Impersonation support (delegation)
111+
if "with_subject" in query:
112+
with_subject = query.pop('with_subject')
113+
109114
# if only these "non-config" values were present, the dict will now be empty
110115
if not query:
111116
# if a dataset_id exists, we need to return a job_config that isn't None
@@ -120,6 +125,7 @@ def parse_url(url): # noqa: C901
120125
credentials_base64,
121126
QueryJobConfig(),
122127
list_tables_page_size,
128+
with_subject,
123129
user_supplied_client,
124130
)
125131
else:
@@ -132,6 +138,7 @@ def parse_url(url): # noqa: C901
132138
credentials_base64,
133139
None,
134140
list_tables_page_size,
141+
with_subject,
135142
user_supplied_client,
136143
)
137144

@@ -282,5 +289,6 @@ def parse_url(url): # noqa: C901
282289
credentials_base64,
283290
job_config,
284291
list_tables_page_size,
292+
with_subject,
285293
user_supplied_client,
286294
)

tests/unit/test_parse_url.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def url_with_everything():
6464
"&use_query_cache=true"
6565
"&write_disposition=WRITE_APPEND"
6666
"&user_supplied_client=true"
67+
"&with_subject=user@foo.com"
6768
)
6869

6970

@@ -78,6 +79,7 @@ def test_basic(url_with_everything):
7879
job_config,
7980
list_tables_page_size,
8081
user_supplied_client,
82+
with_subject,
8183
) = parse_url(url_with_everything)
8284

8385
assert project_id == "some-project"
@@ -89,6 +91,7 @@ def test_basic(url_with_everything):
8991
assert credentials_base64 == "eyJrZXkiOiJ2YWx1ZSJ9Cg=="
9092
assert isinstance(job_config, QueryJobConfig)
9193
assert user_supplied_client
94+
assert with_subject
9295

9396

9497
@pytest.mark.parametrize(
@@ -191,6 +194,7 @@ def test_empty_with_non_config():
191194
job_config,
192195
list_tables_page_size,
193196
user_supplied_credentials,
197+
with_subject,
194198
) = url
195199

196200
assert project_id is None
@@ -202,6 +206,7 @@ def test_empty_with_non_config():
202206
assert job_config is None
203207
assert list_tables_page_size is None
204208
assert not user_supplied_credentials
209+
assert not with_subject
205210

206211

207212
def test_only_dataset():
@@ -215,6 +220,7 @@ def test_only_dataset():
215220
credentials_base64,
216221
job_config,
217222
list_tables_page_size,
223+
with_subject,
218224
user_supplied_credentials,
219225
) = url
220226

@@ -227,6 +233,7 @@ def test_only_dataset():
227233
assert list_tables_page_size is None
228234
assert isinstance(job_config, QueryJobConfig)
229235
assert not user_supplied_credentials
236+
assert not user_supplied_credentials
230237
# we can't actually test that the dataset is on the job_config,
231238
# since we take care of that afterwards, when we have a client to fill in the project
232239

0 commit comments

Comments
 (0)