2016-11-28 20 views
13

Tôi đã tham gia xây dựng một ứng dụng sử dụng nội bộ mà qua đó người dùng có thể tải tệp lên, được lưu trữ trong Google Drive. Vì không nên sử dụng tài khoản dịch vụ làm chủ sở hữu tệp, tôi muốn tải lên ứng dụng thay mặt cho tài khoản người dùng được chỉ định mà công ty sysadmin có quyền truy cập.Thất bại trong việc ủy ​​quyền truy cập Google Drive vào tài khoản dịch vụ

Tôi đã tạo ứng dụng cùng với tài khoản dịch vụ. Có hai phím tạo cho tài khoản dịch vụ, như tôi đã thử cả JSON và các định dạng pkcs12 cố gắng thực hiện điều này:

API Manager Credentials page

tôi đã tải OAuth 2.0 chi tiết ID của khách hàng, và cũng có .json và các file đuôi .p12 cho các phím tài khoản dịch vụ (theo thứ tự mà như được hiển thị ở trên):

Downloaded files for client and service accounts

tôi đã sysadmin của tôi đi qua các bước chi tiết tại đây để uỷ quyền cho truy cập Drive API để tài khoản dịch vụ: https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account

Chúng tôi nhận thấy rằng điều duy nhất hoạt động cho "Tên khách hàng" ở bước 4 là "ID ứng dụng khách" được liệt kê cho ứng dụng web (kết thúc .apps.googleusercontent.com). Các ID hexadecimal dài liệt kê cho các phím tài khoản dịch vụ không gì nó yêu cầu (xem dưới đây):

Authorized API client access

Trước đây đến ở trên, tôi đã có mã số đó sẽ tạo ra một trường hợp DriveService có thể tải trực tiếp lên dịch vụ tài khoản, tham khảo các tập tin .json cho khóa tài khoản dịch vụ:

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; 
} 

đó làm việc niêm yết và tải lên, mặc dù tất nhiên không có giao diện web để truy cập vào các tập tin, và có vẻ như là mặc dù nó không xử lý những thứ như siêu dữ liệu quyền hoặc tạo hình thu nhỏ cho ví dụ PDF. Đây là lý do tại sao tôi đang cố gắng sử dụng tài khoản chuẩn cho video tải lên. Khi đã được phân loại rõ ràng, sau đó tôi đã cố gắng điều chỉnh mã được hiển thị trong tham chiếu ủy nhiệm được liên kết ở trên, kết hợp với mã từ nơi khác để trích xuất các chi tiết cần thiết từ tệp khóa .json. Với mã này, ngay sau khi tôi cố gắng để thực hiện bất kỳ lệnh API, thậm chí đơn giản như:

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

tôi nhận được một lỗi:

Ngoại lệ chi tiết: Google.Apis.Auth.OAuth2.Responses.TokenResponseException : Lỗi: "unauthorized_client", mô tả: "client trái phép hoặc phạm vi trong yêu cầu.", Uri: ""

mã cho nỗ lực đó là:

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; 
} 

Cuối cùng, tôi đã tạo ra thứ hai se rvice khóa tài khoản để lưu a.tập p12 để phù hợp chặt chẽ hơn các mã trong tài liệu hướng dẫn phái đoàn chính quyền, nhưng mà kết quả trong cùng một ngoại lệ:

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; 
} 

Lớp liên quan minimial mà phương pháp này sống là:

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(); 
    } 
} 

Và đó là được sử dụng trong thời trang này:

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

Trường hợp ngoại lệ xảy ra trên dòng cuối cùng đó.

Tôi không hiểu tại sao tài khoản dịch vụ của tôi không thể hành động thay mặt cho người dùng được chỉ định, là một phần của tên miền mà tài khoản dịch vụ của ứng dụng phải được ủy quyền. Tôi đã hiểu lầm điều gì ở đây?

+0

Đây có thể là lỗi cấu hình. Bạn đã nhập ID ứng dụng của tài khoản dịch vụ trong tên Khách hàng chưa? Đó là bướC# 5 trong [Ủy quyền toàn quyền miền cho tài khoản dịch vụ của bạn] (https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account) – noogui

+0

@noogui Tệp .json được tạo cho khóa tài khoản dịch vụ chứa mục nhập "client_id", là số thập phân gồm 21 chữ số. Sử dụng cho bướC# 5 cho thấy một thông báo lỗi: "Tên khách hàng này chưa được đăng ký với Google." –

Trả lời

6

Tôi đã tự tìm thấy câu trả lời và là cấu hình chứ không phải mã. Liên kết tôi đã chia sẻ với các bước để ủy quyền không đề cập đến tùy chọn có sẵn khi tạo tài khoản dịch vụ: hộp kiểm cho biết tài khoản sẽ đủ điều kiện cho ủy quyền trên toàn miền (DwD).

liên kết này mô tả việc tạo tài khoản dịch vụ và đoàn đại biểu chính xác hơn: https://developers.google.com/identity/protocols/OAuth2ServiceAccount

Tôi không biết về DWD khi tôi tạo tài khoản dịch vụ, và vì vậy tôi đã không chọn tùy chọn đó. Có thể quay lại và chỉnh sửa tài khoản dịch vụ để chọn nó. Khi tôi đã thực hiện việc này, tôi có thể truy xuất một ID ứng dụng khách chính xác để sử dụng trong phần "Quản lý quyền truy cập ứng dụng API" của bảng điều khiển dành cho quản trị viên. Sử dụng phương thức GetServiceC() sau đó hoạt động như dự định và tôi có thể truy xuất tệp cho người dùng trong cùng miền Apps.

Đây là hộp kiểm mà cần phải được đánh dấu cho một tài khoản dịch vụ để đủ điều kiện cho đoàn đại biểu miền toàn quyền:

Creation of a new service account - DwD checkbox

Đây là thông tin thêm có sẵn khi bạn đã làm điều đó (với một tài khoản dịch vụ throwaway bên cạnh đó đã không đã hộp đánh dấu, để đối chiếu):

Comparison of service accounts with and without DwD

+0

Tôi đã không thấy tùy chọn ủy quyền tên miền mới trên giao diện người dùng quan trọng và không bao giờ cần nó trước đó - nó phải là thuộc tính mới. Vui mừng bạn đã nhận nó được sắp xếp! –

-2

Hầu hết tất cả mọi thứ trông ok nhưng:

A. Sử dụng ServiceC mã, không chắc chắn nếu các vấn đề đối tượng đánh máy nhưng dòng của bạn:

var credential = new ServiceAccountCredential... 

nên

ServiceAccountCredential credential = new ServiceAccountCredential... 

B. Kiểm tra tệp P12 trong ServiceC là tệp P12 thực sự mà bạn đã tải lên môi trường của mình nơi bạn đang chạy tệp này.

C. cập nhật câu hỏi của bạn bằng mã có thể chạy chính xác mà bạn đang sử dụng để tạo và gọi dịch vụ của bạn: filelist: execute code. Bằng cách này, rõ ràng hơn và ít giả định hơn.

+0

A. Sử dụng 'var' sẽ không tạo sự khác biệt, xem https://msdn.microsoft.com/en-us/library/bb384061.aspx –

+0

(B) Tôi đang chạy nó trên máy cục bộ của mình; Tôi đã làm rõ mã để cho thấy rằng tôi đang tải nó trực tiếp từ thư mục App_Data. (C) Tôi đã thêm mã thực tế để thực hiện lệnh FileList (phổ biến cho tất cả các phương thức GetService()) –

1

Bạn có thể chọn hộp kiểm Bật ủy quyền trên toàn G Suite khi bạn tạo tài khoản dịch vụ trên bảng quản trị.

Kính trọng

+0

Cảm ơn, nhưng như bạn có thể thấy tôi đã quản lý để khám phá nơi tôi đã đi sai! :) –

Các vấn đề liên quan