GAE AttributeError: 'Credentials' 对象没有属性 'with_subject'

mar*_*gas 5 python google-app-engine google-authentication google-api-python-client

我有一个 Python 应用程序,我想部署在 App Engine(第二代 Python 3.7)上,我在该应用程序上使用启用了域范围委派的服务帐户来访问用户数据。

在本地我这样做:

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)
Run Code Online (Sandbox Code Playgroud)

这很好用,据我所知,这是当前使用应用程序默认凭据时执行此操作的方法(在本地我定义了 GOOGLE_APPLICATION_CREDENTIALS)。

问题出在 GAE 上,部署后,调用会with_subject引发: AttributeError: 'Credentials' object has no attribute 'with_subject'

我已经在 GAE 服务帐户上启用了域范围的委派。

当我在本地使用的 GOOGLE_APPLICATION_CREDENTIALS 和 GAE 中的那些都是具有域范围委派的服务帐户时,它们之间有什么区别?

.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'"  
Run Code Online (Sandbox Code Playgroud)

套餐(部分清单):

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
Run Code Online (Sandbox Code Playgroud)

vko*_*pio 8

确实,您无法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]
Run Code Online (Sandbox Code Playgroud)

现在来看看实际的 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)
Run Code Online (Sandbox Code Playgroud)

如果您设置了带有服务帐户文件路径的环境变量,则该try块不会失败。GOOGLE_APPLICATION_CREDENTIALS如果应用程序在 Google Cloud 上运行,则会出现AttributeError,并且通过创建具有正确subject和 的新凭据来处理它scopes

您还可以None作为subjectfordelegated_credentials函数传递,它会在没有委托的情况下创建凭据,因此可以在有或没有委托的情况下使用该函数。

  • 我收到以下错误::“凭据”对象没有属性“service_account_email”。仅供参考,我使用联合身份验证凭据:``from google.auth import aws as google_auth_awscredentials = google_auth_aws.Credentials.from_info(json_config_info)``` (2认同)