GAE AttributeError:“凭据”对象没有属性“ with_subject”
问题内容:
我有一个要部署在App Engine(第二代Python 3.7)上的python应用程序,在该引擎上我使用了启用了域范围委派的服务帐户来访问用户数据。
我在本地做:
import google.auth
from apiclient.discovery import build
creds, project = google.auth.default(
scopes=['https://www.googleapis.com/auth/admin.directory.user', ],
)
creds = creds.with_subject(GSUITE_ADMIN_USER)
service = build('admin', 'directory_v1', credentials=creds)
据我所知,这很好用,这是使用“应用默认凭据”(在本地,我定义了GOOGLE_APPLICATION_CREDENTIALS)时的 当前 方法。
问题出在GAE上,部署后with_subject
会引发以下问题: AttributeError: 'Credentials' object has no attribute 'with_subject'
我已经在GAE服务帐户上启用了域范围的委派。
当我在本地使用的GOOGLE_APPLICATION_CREDENTIALS和GAE中的GOOGLE_APPLICATION_CREDENTIALS都是具有域范围委派的服务帐户时,有什么区别?
.with_subject()
GAE在哪里?
creds
收到的对象是类型compute_engine.credentials.Credentials
。
完整回溯:
Traceback (most recent call last):
File "/env/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/env/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 104, in init_process
super(ThreadWorker, self).init_process()
File "/env/lib/python3.7/site-packages/gunicorn/workers/base.py", line 129, in init_process
self.load_wsgi()
File "/env/lib/python3.7/site-packages/gunicorn/workers/base.py", line 138, in load_wsgi
self.wsgi = self.app.wsgi()
File "/env/lib/python3.7/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/env/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 52, in load
return self.load_wsgiapp()
File "/env/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 41, in load_wsgiapp
return util.import_app(self.app_uri)
File "/env/lib/python3.7/site-packages/gunicorn/util.py", line 350, in import_app
__import__(module)
File "/srv/main.py", line 1, in <module>
from config.wsgi import application
File "/srv/config/wsgi.py", line 38, in <module>
call_command('gsuite_sync_users')
File "/env/lib/python3.7/site-packages/django/core/management/__init__.py", line 148, in call_command
return command.execute(*args, **defaults)
File "/env/lib/python3.7/site-packages/django/core/management/base.py", line 353, in execute
output = self.handle(*args, **options)
File "/srv/metanube_i4/users/management/commands/gsuite_sync_users.py", line 14, in handle
gsuite_sync_users()
File "/env/lib/python3.7/site-packages/celery/local.py", line 191, in __call__
return self._get_current_object()(*a, **kw)
File "/env/lib/python3.7/site-packages/celery/app/task.py", line 375, in __call__
return self.run(*args, **kwargs)
File "/srv/metanube_i4/users/tasks.py", line 22, in gsuite_sync_users
creds = creds.with_subject(settings.GSUITE_ADMIN_USER)
AttributeError: 'Credentials' object has no attribute 'with_subject'"
套餐(部分清单):
google-api-core==1.5.0
google-api-python-client==1.7.4
google-auth==1.5.1
google-auth-httplib2==0.0.3
google-cloud-bigquery==1.6.0
google-cloud-core==0.28.1
google-cloud-logging==1.8.0
google-cloud-storage==1.13.0
google-resumable-media==0.3.1
googleapis-common-protos==1.5.3
httplib2==0.11.3
oauthlib==2.1.0
问题答案:
的确,您不能使用with_subject
具有GAE或GCE凭据的方法。但是,有一种解决方法可以在GCE服务器上工作,并且我认为这也适用于GAE默认服务帐户。解决方案是使用带有subject
和的服务帐户标识来构建新的凭据scopes
。可以在此处找到详细的指南,但下面还将介绍该过程。
首先,服务帐户需要权限才能为其自身创建服务帐户令牌。这可以通过转到项目IAM and admin > Service accounts
页面来完成(确保信息面板可见,可以从右上角进行切换)。复制服务帐户的电子邮件地址,并通过选中复选框来选择有问题的服务帐户。现在信息面板上应该有ADD MEMBER
按钮。单击它,然后将服务帐户电子邮件粘贴到New members
文本框中。单击Select role
下拉列表,然后选择角色Service Accounts -> Service Account Token Creator
。您可以使用以下gcloud
命令检查是否已分配角色:
gcloud iam service-accounts get-iam-policy [SERVICE_ACCOUNT_EMAIL]
现在到实际的Python代码。本示例是对上面链接的文档的略微修改。
from googleapiclient.discovery import build
from google.auth import default, iam
from google.auth.transport import requests
from google.oauth2 import service_account
TOKEN_URI = 'https://accounts.google.com/o/oauth2/token'
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
GSUITE_ADMIN_USER = 'admin@example.com'
def delegated_credentials(credentials, subject, scopes):
try:
# If we are using service account credentials from json file
# this will work
updated_credentials = credentials.with_subject(subject).with_scopes(scopes)
except AttributeError:
# This exception is raised if we are using GCE default credentials
request = requests.Request()
# Refresh the default credentials. This ensures that the information
# about this account, notably the email, is populated.
credentials.refresh(request)
# Create an IAM signer using the default credentials.
signer = iam.Signer(
request,
credentials,
credentials.service_account_email
)
# Create OAuth 2.0 Service Account credentials using the IAM-based
# signer and the bootstrap_credential's service account email.
updated_credentials = service_account.Credentials(
signer,
credentials.service_account_email,
TOKEN_URI,
scopes=scopes,
subject=subject
)
except Exception:
raise
return updated_credentials
creds, project = default()
creds = delegated_credentials(creds, GSUITE_ADMIN_USER, SCOPES)
service = build('admin', 'directory_v1', credentials=creds)
try
如果您GOOGLE_APPLICATION_CREDENTIALS
设置的环境变量带有服务帐户文件的路径,则该块不会失败。如果该应用程序在Google
Cloud上运行,则会出现一个,AttributeError
并通过创建具有subject
和的新凭据来进行处理scopes
。
您也可以通过None
为subject
对delegated_credentials
功能,不受代表团创建凭证,以便该功能可有或无授权使用。