Подтвердить что ты не робот

Неспособность делегировать доступ Google Диска к учетной записи службы

Я занимаюсь созданием внутреннего приложения, через которое пользователи могут загружать файлы, которые будут храниться на Google Диске. Поскольку рекомендуется не использовать учетные записи служб в качестве владельцев файлов, я хотел, чтобы приложение загружалось от имени назначенной учетной записи пользователя, к которой имеет доступ sysadmin компании.

Я создал приложение вместе с учетной записью службы. Для учетной записи службы есть два ключа, поскольку я попытался использовать форматы JSON и PKCS12, пытаясь добиться этого:

Страница учетных записей API-менеджера

Я загрузил данные идентификатора клиента OAuth 2.0, а также файлы .json и .p12 для ключей учетной записи службы (в указанном выше порядке):

Загруженные файлы для учетных записей клиентов и служб

У меня был мой системный администратор, пройдя шаги, подробно описанные здесь, чтобы делегировать полномочия для доступа к API-интерфейсу API для учетной записи службы: https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account p >

Мы обнаружили, что единственное, что работало для "имени клиента" на шаге 4, - это "Идентификатор клиента", указанный для веб-приложения (завершение .apps.googleusercontent.com). Длинные шестнадцатеричные идентификаторы, перечисленные для ключей учетной записи службы, не соответствуют требованиям (см. Ниже):

Доступ к авторизированному API-интерфейсу API

Раньше к вышесказанному у меня был код, который создавал бы экземпляр DriveService, который мог бы загружаться непосредственно в учетную запись службы, ссылаясь на файл .json для ключей учетной записи службы:

private DriveService GetServiceA()
{
    var settings = SettingsProvider.GetInstance();

    string keyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.json");
    var scopes = new string[] { DriveService.Scope.Drive };

    var stream = new IO.FileStream(keyFilePath, IO.FileMode.Open, IO.FileAccess.Read);
    var credential = GoogleCredential.FromStream(stream);
    credential = credential.CreateScoped(scopes);

    var service = new DriveService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential,
        ApplicationName = "MyAppName"
    });

    return service;
}

Это работает для листинга и загрузки, хотя, конечно, нет веб-интерфейса для доступа к файлам, и кажется, что он не обрабатывает такие вещи, как метаданные разрешений или генерирование эскизов, например. PDF файлы. Вот почему я пытаюсь использовать стандартную учетную запись для загрузок.

Как только делегирование было, по-видимому, отсортировано, я попытался адаптировать код, показанный в ссылке, приведенной выше, в сочетании с кодом из другого места для извлечения необходимых данных из файла ключа .json. С помощью этого кода, как только я попытаюсь выполнить любую команду API, так же просто, как:

FileList fileList = service.FileList().Execute();

Я получаю сообщение об ошибке:

Сведения об исключении: Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Ошибка: "unauthorized_client", Описание: "Неавторизованный клиент или область в запросе". Uri: ""

Код для этого усилия:

private DriveService GetServiceB()
{
  var settings = SettingsProvider.GetInstance();

  string keyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.json");
  string serviceAccountEmail = "<account-email>@<project-id>.iam.gserviceaccount.com";
  var scopes = new string[] { DriveService.Scope.Drive };

  var stream = new IO.FileStream(keyFilePath, IO.FileMode.Open, IO.FileAccess.Read);
  var reader = new IO.StreamReader(stream);
  string jsonCreds = reader.ReadToEnd();
  var o = JObject.Parse(jsonCreds);
  string privateKey = o["private_key"].ToString();

  var credential = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
      Scopes = scopes,
      User = "[email protected]"
    }
    .FromPrivateKey(privateKey)
  );

  var service = new DriveService(new BaseClientService.Initializer()
  {
    HttpClientInitializer = credential,
    ApplicationName = "MyAppName"
  });

  return service;
}

Наконец, я создал второй ключ учетной записи службы, чтобы сохранить файл .p12, чтобы более точно сопоставлять код с документацией делегирования полномочий, но который приводит к тому же исключению:

private DriveService GetServiceC()
{
  var settings = SettingsProvider.GetInstance();

  string p12KeyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.p12");
  string serviceAccountEmail = "<account-email>@<project-id>.iam.gserviceaccount.com";
  var scopes = new string[] { DriveService.Scope.Drive }; // Full access

  X509Certificate2 certificate = new X509Certificate2(
    p12KeyFilePath,
    "notasecret",
    X509KeyStorageFlags.Exportable
  );

  var credential = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
      Scopes = scopes,
      User = "[email protected]"
    }
    .FromCertificate(certificate)
  );

  var service = new DriveService(new BaseClientService.Initializer()
  {
    HttpClientInitializer = credential,
    ApplicationName = "MyAppName"
  });

  return service;
}

Минимальный соответствующий класс, в котором живет этот метод:

public class GoogleDrive
{
  public DriveService Service { get; private set; }

  public GoogleDrive()
  {
    this.Service = this.GetService();
  }

  private DriveService GetService()
  {
    // Code from either A, B or C
  }

  public FilesResource.ListRequest FileList()
  {
    return this.Service.Files.List();
  }
}

И используется таким образом:

var service = new GoogleDrive();
FilesResource.ListRequest listRequest = service.FileList();
FileList fileList = listRequest.Execute();

Исключение происходит в этой последней строке.

Я не понимаю, почему моя учетная запись службы не может действовать от имени назначенного пользователя, который является частью домена, для которого учетная запись службы приложений должна иметь делегированные полномочия. Что я здесь неправильно понял?

4b9b3361

Ответ 1

Я сам нашел ответ, и это была конфигурация, а не код. В ссылке, которую я разделял с шагами для делегирования полномочий, не упоминается опция, доступная при создании учетной записи службы: флажок, указывающий, что учетная запись будет иметь право на делегирование домена (DwD).

Эта ссылка описывает создание и делегирование учетной записи более точно: https://developers.google.com/identity/protocols/OAuth2ServiceAccount

Я не знал о DwD, когда создал учетную запись службы, и поэтому я не выбрал эту опцию. Для его выбора можно вернуться и отредактировать учетную запись службы. Как только я это сделал, мне удалось получить правильный идентификатор клиента для использования в разделе "Управление клиентским доступом к API" в консоли администратора. Используя метод GetServiceC(), он работает так, как предполагалось, и я могу получить файлы для пользователей в том же домене Apps.

Это флажок, который нужно отметить для учетной записи службы, чтобы иметь право на передачу полномочий домена:

Создание новой учетной записи службы - флажок DwD

Это дополнительная информация, доступная после того, как вы это сделали (с помощью вспомогательной учетной записи службы, рядом с которой не было поля для галочки):

Сравнение учетных записей службы с и без DwD

Ответ 2

При создании учетной записи службы на панели администратора вы можете пометить флажок Включить делегирование домена G Suite во всей домене.

Привет

Ответ 3

Большинство из них выглядит нормально, но:

а. Используйте код ServiceC, не уверен, что вопрос о наборе объектов имеет значение, кроме вашей строки:

var credential = new ServiceAccountCredential...

должен быть

ServiceAccountCredential credential = new ServiceAccountCredential...

В. Убедитесь, что файл P12 в ServiceC - это реальный файл P12, который вы фактически загрузили в среду, в которой вы работаете.

С. обновите свой вопрос с помощью точного исполняемого кода, который вы используете для создания и вызова вашей службы: filelist: выполнить код. Таким образом, есть более ясность и меньше предположений.