@@ -112,6 +112,10 @@ class Credentials(credentials.Signing, credentials.Scoped, credentials.Credentia
112
112
113
113
scoped_credentials = credentials.with_scopes(['email'])
114
114
delegated_credentials = credentials.with_subject(subject)
115
+
116
+ To add a quota project, use :meth:`with_quota_project`::
117
+
118
+ credentials = credentials.with_quota_project('myproject-123')
115
119
"""
116
120
117
121
def __init__ (
@@ -122,6 +126,7 @@ def __init__(
122
126
scopes = None ,
123
127
subject = None ,
124
128
project_id = None ,
129
+ quota_project_id = None ,
125
130
additional_claims = None ,
126
131
):
127
132
"""
@@ -135,6 +140,8 @@ def __init__(
135
140
user to for which to request delegated access.
136
141
project_id (str): Project ID associated with the service account
137
142
credential.
143
+ quota_project_id (Optional[str]): The project ID used for quota and
144
+ billing.
138
145
additional_claims (Mapping[str, str]): Any additional claims for
139
146
the JWT assertion used in the authorization grant.
140
147
@@ -150,6 +157,7 @@ def __init__(
150
157
self ._service_account_email = service_account_email
151
158
self ._subject = subject
152
159
self ._project_id = project_id
160
+ self ._quota_project_id = quota_project_id
153
161
self ._token_uri = token_uri
154
162
155
163
if additional_claims is not None :
@@ -229,6 +237,11 @@ def project_id(self):
229
237
"""Project ID associated with this credential."""
230
238
return self ._project_id
231
239
240
+ @property
241
+ def quota_project_id (self ):
242
+ """Project ID to use for quota and billing purposes."""
243
+ return self ._quota_project_id
244
+
232
245
@property
233
246
def requires_scopes (self ):
234
247
"""Checks if the credentials requires scopes.
@@ -247,6 +260,7 @@ def with_scopes(self, scopes):
247
260
token_uri = self ._token_uri ,
248
261
subject = self ._subject ,
249
262
project_id = self ._project_id ,
263
+ quota_project_id = self ._quota_project_id ,
250
264
additional_claims = self ._additional_claims .copy (),
251
265
)
252
266
@@ -267,6 +281,7 @@ def with_subject(self, subject):
267
281
token_uri = self ._token_uri ,
268
282
subject = subject ,
269
283
project_id = self ._project_id ,
284
+ quota_project_id = self ._quota_project_id ,
270
285
additional_claims = self ._additional_claims .copy (),
271
286
)
272
287
@@ -292,9 +307,32 @@ def with_claims(self, additional_claims):
292
307
token_uri = self ._token_uri ,
293
308
subject = self ._subject ,
294
309
project_id = self ._project_id ,
310
+ quota_project_id = self ._quota_project_id ,
295
311
additional_claims = new_additional_claims ,
296
312
)
297
313
314
+ def with_quota_project (self , quota_project_id ):
315
+ """Returns a copy of these credentials with a modified quota project.
316
+
317
+ Args:
318
+ quota_project_id (str): The project to use for quota and
319
+ billing purposes
320
+
321
+ Returns:
322
+ google.auth.service_account.Credentials: A new credentials
323
+ instance.
324
+ """
325
+ return self .__class__ (
326
+ self ._signer ,
327
+ service_account_email = self ._service_account_email ,
328
+ scopes = self ._scopes ,
329
+ token_uri = self ._token_uri ,
330
+ subject = self ._subject ,
331
+ project_id = self ._project_id ,
332
+ quota_project_id = quota_project_id ,
333
+ additional_claims = self ._additional_claims .copy (),
334
+ )
335
+
298
336
def _make_authorization_grant_assertion (self ):
299
337
"""Create the OAuth 2.0 assertion.
300
338
@@ -335,6 +373,12 @@ def refresh(self, request):
335
373
self .token = access_token
336
374
self .expiry = expiry
337
375
376
+ @_helpers .copy_docstring (credentials .Credentials )
377
+ def apply (self , headers , token = None ):
378
+ super (Credentials , self ).apply (headers , token = token )
379
+ if self .quota_project_id is not None :
380
+ headers ["x-goog-user-project" ] = self .quota_project_id
381
+
338
382
@_helpers .copy_docstring (credentials .Signing )
339
383
def sign_bytes (self , message ):
340
384
return self ._signer .sign (message )
0 commit comments