Неспособность делегировать доступ 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();
Исключение происходит в этой последней строке.
Я не понимаю, почему моя учетная запись службы не может действовать от имени назначенного пользователя, который является частью домена, для которого учетная запись службы приложений должна иметь делегированные полномочия. Что я здесь неправильно понял?
Ответы
Ответ 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: выполнить код. Таким образом, есть более ясность и меньше предположений.