How can an authenticated user access the drive that is publicly accessible? - c#

With this code, I am able to get the files that have been shared to the service account email.
But, when I shared the folder (that was not owned by me, but is publicly accessible) from a different email it is not displayed in the list.
Is there any way an authenticated user can access the drive folder that is publicly accessible, but which I won't own?
var serviceAccountEmail = "";
var certificate = new X509Certificate2(_credentialsService.GetCredentialPath(), "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { DriveService.Scope.Drive }
}.FromCertificate(certificate));
var service = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credential
});
Getting Drive Details
Google.Apis.Drive.v3.FilesResource.ListRequest FileListRequest = service.Files.List();
// for getting folders only.
//FileListRequest.Q = "mimeType='application/vnd.google-apps.folder'";
FileListRequest.Fields = "nextPageToken, files(id, name)";
FileListRequest.Corpora = "allDrives";
FileListRequest.Q = "sharedWithMe";
FileListRequest.IncludeItemsFromAllDrives = true;
FileListRequest.SupportsAllDrives = true;
// List files.
IList<Google.Apis.Drive.v3.Data.File> files = FileListRequest.Execute().Files;
List<GoogleDriveFile> FileList = new List<GoogleDriveFile>();
Sharing Process:
I shared the file to the service account email, from my personal google email.
And See it is still not available, the image that I owned only is shared. Which is my problem.

Sending the link of a publicly viewable file is not the same as sharing the file
Your screen looks like this:
This means that you have view-only access to the file
If you had edit access to the file, the screen would look as following:
Thus, since you are not an editor of the public file, you cannot add another viewer / editor to this file and consecuently it will not appear in the sharedWithMe of the user to whom you sent the email - no matter if this user is an actual suer or a service account
Solution:
If you need to explicitly share the file with the service account, your options are
to obtain edit access to the public file (if feasible)
create a copy of the respective file onto your own Drive (if copying is not disabled) and share your version explicitly with the service account

Related

How to safely store OAuth response file of each user provided by google drive api

I am working on an asp.net application where I ask people to give the authorization (using OAuth) for accessing their google drive (to a particular folder) to be able to list the files within the application.
The following code enables users to provide authorization and creates a corresponding Google.Apis.Auth.OAuth2.Responses file in the server to be used for future requests. But, this request will happen for each user, which will create more OAuth response files. I am not sure how to design the application and store these files safely. Probably, I may create a new folder (using the Guid-based UserIds) for each user and save the file in that folder. Does this make sense? Or do you recommend another approach?
using (var stream =
new FileStream("Services/credentials.json", FileMode.Open, FileAccess.Read))
{
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
BaseClientService.Initializer bcs = new BaseClientService.Initializer();
bcs.HttpClientInitializer = credential;
DriveService service = new DriveService(bcs);
Correction
I am working on an asp.net application where I ask people to give the authorization (using OAuth) for accessing their google drive (to a particular folder) to be able to list the files within the application.
When you request access Its going to be for the users full drive account its not going to be just for one folder.
Storing credential files.
Your question is a bit hard to understand but I think you are asking where you should store your credential files. The way FileDataStore works is that it creates a new file for each "user" so depending upon what you have set for "user" a file will be created as you can see i tend to use a guid at the end and store that in a session var that way i know when the user comes back with this session var its that file i need to grab for them. Actually the client library does all that for you because as soon as it sees the id it will use that automatically.
Web vs installed application.
I do have one major comment if you are using Asp .net and intend to use a web application then you are using the wrong code. The following sample shows how to authenticate using Web appliations asp.net mvc note the session in the code below.
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "PUT_CLIENT_ID_HERE",
ClientSecret = "PUT_CLIENT_SECRET_HERE"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}

Get secure file download ticket via .NET API

Using the Microsoft.TeamFoundation .NET client APIs, how can I get a download ticket for a secure file from TFS 2017u2?
Using the Microsoft.TeamFoundation.DistributedTasks.WebApi nuget package, I puffed up the below code in a console app. It successfully retrieves all the metadata and associated properties entered in TFS for the secure file. However, despite passing the "includeDownloadTicket" argument as "true" to the GetSecureFileAsync method, the Ticket property is always null.
I thought perhaps it was a permissions issue but I am in the TFS Admin role and also specifically assigned myself as an admin to the file entry in the web interface.
var credentials = new VssCredentials();
var projectName = "{myProjectName}";
var secureFileId = new Guid("{theSecureFileId}");
var tfsUri = new Uri("https://{tfsBox}/{collection}");
var connection = new VssConnection(tfsUri, credentials);
var taskAgentClient = connection.GetClient<TaskAgentHttpClient>();
var projectClient = connection.GetClient<ProjectHttpClient>();
var project = await projectClient.GetProject(projectName, true);
var secureFile = await taskAgentClient.GetSecureFileAsync(project.Id, secureFileId, true);
var secureFileTicket = secureFile.Ticket;
if (secureFileTicket == null)
{
Console.WriteLine(
"No download ticket was provided by the TFS for the requested Secure File.");
return;
}
Your thought is right. This is related to the permission. For security reason, normal users are limited to download the secure files. The permission you can set is "Reader", "User" and "Administrator" for normal users while it requires "ViewSecrets" permission to include the download ticket.
So you cannot download the secure files for now.

Google Drive API - Transfer ownership from Service Account

I am attempting to transfer ownership from a Service Account created document to another user who resides within the same Google Apps account using the code below but am getting the following error
The resource body includes fields which are not directly writable. [403]
Errors [Message[The resource body includes fields which are not directly writable.] Location[ - ] Reason[fieldNotWritable] Domain[global]]
var service = GetService();
try
{
var permission = GetPermission(fileId, email);
permission.Role = "owner";
var updatePermission = service.Permissions.Update(permission, fileId, permission.Id);
updatePermission.TransferOwnership = true;
return updatePermission.Execute();
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
return null;
Commenting out // permission.Role = "owner"; returns the error below
The transferOwnership parameter must be enabled when the permission role is 'owner'. [403] Errors [Message[The transferOwnership parameter must be enabled when the permission role is 'owner'.] Location[transferOwnership - parameter] Reason[forbidden] Domain[global]]
Assigning any other permissions works fine. Therefore, is this a limitation of the Service Account not being able to transfer ownership to any other account that doesn't use the #gserviceaccount.com email address (i.e. our-project#appspot.gserviceaccount.com > email#domain.com)?
The email#domain.com email address has been created and is managed within Google Apps.
In the case, it is not achievable, any pointers on where to look next? We need multiple users to have the ability to create documents ad hoc and assign permissions and transfer ownership on the fly via the API.
Thanks
I have found the answer and am posting for anyone else who comes across this question.
You can not use the 'Service Account Key JSON file' as recommended by Google.
You need to use the p.12 certificate file for authentication.
The code to create a drive service for mimicking accounts is as follows.
public DriveService GetService(string certificatePath, string certificatePassword, string googleAppsEmailAccount, string emailAccountToMimic, bool allowWrite = true)
{
var certificate = new X509Certificate2(certificatePath, certificatePassword, X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(googleAppsEmailAccount)
{
Scopes = new[] { allowWrite ? DriveService.Scope.Drive : DriveService.Scope.DriveReadonly },
User = emailAccountToMimic
}.FromCertificate(certificate));
// Create the service.
return new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
}
You need to follow the steps listed here to delegate domain-wide authority to the service account.
Allow 5 to 10 minutes after completing step 4.
You can now create documents under the 'emailAccountToMimic' user which sets them to be the owner during creation.
I don't think it is possible to transfer the ownership from a non-ServiceAccount to a ServiceAccount, vice versa.
If you do that interactively, you will get the below error:
Typically, the document can be created and owned by the users and ownership transfer can be done using their own credentials. You will also have the option to impersonate as the owner if your Service Account is granted with the domain-wide delegation correctly.

Service Account authentication to upload a file to Google Drive

I need to upload a file to Google Drive from my Web Application. I need to use Service Account authentication. I have read Google Drive guide here: DotNetQuickStart and this tutorial for authentication Authentication Google Drive MVC but i don't know what i need to do.
In order to Google Drive guide i have set the parameter to Google Developers Page.
I have download and implemented secret.json file to my project and i have add this code to my project:
public static Google.Apis.Drive.v2.Data.File uploadFile(string _uploadFile, string _parent)
{
string[] scopes = new string[] { DriveService.Scope.Drive }; // Full access
var keyFilePath = #"c:\file.p12"; // Downloaded from https://console.developers.google.com
var serviceAccountEmail = "xx#developer.gserviceaccount.com"; // found https://console.developers.google.com
//loading the Key file
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
But i can't understand what to set in keyFilePath and serviceAccountEmail. How can i set this code?Thanks to all
You will need the P12 key file instead of secret.json
Go to https://console.developers.google.com/iam-admin/serviceaccounts --> select your project --> click on the 3 vertical dots located at the right side of the listed Service account --> Create key --> you will see the below dialog

Google API: ServiceAccount errors with DirectoryService and DriveService using C#

What I'm trying to accomplish:
I have been working the past couple days to implement automation into my company's GoogleApps setup. I need to suspend users that have been inactive for 90+ days, deleting them a week after that. Before deletion, I need to backup any files found in their Google Drive.
Why I haven't accomplished it:
I have attempted this with a few different methods. First I was using the Admin SDK to interface with DirectoryServices, listing users, and suspending as necessary. This interfacing would also allow me to delete users when needed. Now, I need only backup their Google Drive files. For this, it's to my understanding that I need a Service Account to impersonate users, download their files, and relocate them (off to a server in bulk storage probably). I don't think this is possible as an Admin programatically because I believe UserCredentials requires user authorization for each user I intend to interface with via the Drive API, manually. On the other hand, a service account should allow me to impersonate users without authorization, provided the proper delegation.
I have created a ServiceAccount, provided it with Domain-Wide-Delegation, enabled the appropriate APIs (Admin SDK, Drive API) through the developer console, and provided the service account with appropriate scope through Google Admin Console security settings. I think that's all the setup I needed to do.
private static async Task<bool> AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
{
if (!File.Exists(keyFilePath)) // make sure the file we're looking for actually exists
{
Console.WriteLine("Could not find ServAcct key file. Authentication failed!");
return false;
}
string[] scopes = new string[] { DirectoryService.Scope.AdminDirectoryUser, DriveService.Scope.Drive };
// Generate certificate using our key file
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
try
{
// Create our credential from our certificate, using scopes
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
// Create our services
dirSvc = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GoogleAppAutomation"
});
drvSvc = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GoogleAppAutomation"
});
} catch(Exception e)
{
Console.WriteLine(e.InnerException);
return false;
}
return true;
}
I am able to authenticate the service account in my code using the SA's email, and the P12 cert. Creation of the DirectoryService and DriveService objects using the ServiceAccountCredential works fine. However, when i go to access the services, I am presented with errors that I have failed to find any help for online.
DirectoryService
// Acquire list of active users
UsersResource.ListRequest req = dirSvc.Users.List();
req.Customer = "my_customer";
req.MaxResults = 500; // 500 is max allowed. Must use paging (.pageToken) for more
req.OrderBy = UsersResource.ListRequest.OrderByEnum.Email;
IList<User> users;
try
{
users = req.Execute().UsersValue; // Execute request for user list
}
catch (Exception e)
{
throw new Exception("User List Exception", e);
}
Sure enough, the catch block will throw an exception containing:
Google.Apis.Requests.RequestError
Domain not found. [404]
Errors [
Message[Domain not found.] Location[ - ] Reason[notFound] Domain[global]
]
DriveService
FilesResource.ListRequest lr = drvSvc.Files.List();
IList<Google.Apis.Drive.v2.Data.File> fl = lr.Execute().Items;
foreach (Google.Apis.Drive.v2.Data.File file in fl)
{
Console.WriteLine("{0}", file.Title);
}
Just as above, the Execute() method throws an exception...
Google.Apis.Requests.RequestError
Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup. [403]
Errors [
Message[Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.] Location[ - ] Reason[dailyLimitExceededUnreg] Domain[usageLimits]
]
Looking in the Developer Console, this API has had no use at all in the Quota log.
So, I suppose my question is a very broad: what am I doing wrong? :(
Edit:
I believe the issue pertaining to Google Drive access relates to a lack of Billing setup. I cannot be sure of this as of yet because my company is being very slow to get such a thing setup. If anyone can confirm or deny this for me, that'd help!
As for the Directory Service issue, I'll just use the Admin SDK to access the users for suspension/deletion, and the service account for Drive backup. I already have the Admin SDK working for this purpose, so no further assistance is needed in that regard (though an explanation of the error would be awesome since it doesn't appear to be documented whatsoever online!)

Categories

Resources