GAE AttributeError: у объекта «Credentials» нет атрибута «with_subject» - PullRequest
0 голосов
/ 08 ноября 2018

У меня есть приложение на python, которое я хочу развернуть на App Engine (2-е поколение 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)

Это работает хорошо, и, насколько я знаю, это текущий способ сделать это при использовании учетных данных приложения по умолчанию (локально я определил 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'"  

Пакеты (неполный список):

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

Ответы [ 2 ]

0 голосов
/ 18 июля 2019

Это правда, что вы не можете использовать метод 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, и она создает учетные данные без делегирования, поэтому эту функцию можно использовать с делегированием или без него.

0 голосов
/ 15 ноября 2018

@marc.fargas Вы можете взглянуть на googleapis / google-auth-library-python library на GitHub. Вы найдете информацию, относящуюся к данному методу:

Полномочия считаются неизменными. Если вы хотите изменить области или предмет, использованный для делегирования, используйте: meth: with_scopes или : Мет: with_subject :: scoped_credentials = credentials.with_scopes (['email']) Delegated_credentials = credentials.with_subject (тема)

Когда вы определили свои учетные данные по умолчанию для приложения с помощью «GOOGLE_APPLICATION_CREDENTIALS», вы получили экземпляр google.auth.service_account.Credentials, который имеет метод with_subject.

Находясь в App Engine, вы вместо этого получаете экземпляр app_engine.Credentials , у которого нет метода with_subject. Это объясняет наблюдаемое поведение и ошибку, которую вы видите.

В соответствии с документацией о делегировании на уровне домена, только учетные данные учетной записи службы могут иметь делегирование на уровне домена.

...