Cannot access files "shared" from service account via drive API? - google-cloud-platform

I have a folder on google drive which was created via a service account, and shared with my user.
When acting as my user, I can see the folder without issue in the UI.
When acting as the service account, I can list all of the files in the folder via API.
My user has been granted "editor" permission of the folder.
What I need to do is access the list of files in the folder via the API with the service account impersionating my user.
I've setup domain wide delegation and included the scopes:
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive
This query returns 100 results when acting as the service account, however when impersonating my user it returns 0 results.
const auth = new google.auth.GoogleAuth({
clientOptions: {
subject: 'myemail#myorganization.com'
},
keyFile: './token.json',
scopes: ['https://www.googleapis.com/auth/drive.file'],
});
drive.files.list({
q: "'THE_FOLDER_ID' in parents",
fields: 'nextPageToken, files(id, name, parents, appProperties)',
includeItemsFromAllDrives: true,
supportsAllDrives: true,
fields: '*',
spaces: 'drive'
}).then(response => {
console.log(response.data.files.length)
})

Turns out I needed to use the scope "https://www.googleapis.com/auth/drive" instead of "https://www.googleapis.com/auth/drive.file"

Related

Is there a way to sync Google Admin directory users list with the people API directory contacts list?

We've been using the google people api to list internal contacts in a custom staff directory on our company's intranet. We've been using the people api so that we can retrieve profile pictures as well as standard profile fields, as the pictures are blocked when using the google Admin SDK. However we've noticed recently that certain contacts are missing from the people API (e.g. new starters).
I've tried using the Admin SDK users.list method to return the users, and can see that the new users definitely exist in the directory, but using the people.listDirectoryPeople method the same users aren't being returned. Is there a way to sync up the directory people contacts list and the admin directory users list? Or is there something I'm missing in my request that's causing me to retrieve old data?
This is a code snippet to show the parameters I'm using with the API (further down there's more code which gets the other pages of results):
authenticate().then((res) => {
let token = res as JWT;
const service = google.people({ version: 'v1', auth: token });
let params: people_v1.Params$Resource$People$Listdirectorypeople = {
readMask: 'addresses,photos,emailAddresses,locations,names,organizations,phoneNumbers,relations',
pageSize: 500,
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE'],
};
service.people.listDirectoryPeople(params, async (err, res) => {

Google sheets API v4 permissions PERMISSION_DENIED error using POSTMAN [duplicate]

I've generated a server key in the API Manager and attempted to execute the following on my Mac:
curl 'https://sheets.googleapis.com/v4/spreadsheets/MySheetID?ranges=A1:B5&key=TheServerKeyIGeneratedInAPIManager'
But this is what it returns:
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
What am I doing wrong here?
To solve this issue, try to:
Create a service account: https://console.developers.google.com/iam-admin/serviceaccounts/
In options, create a key: this key is your usual client_secret.json - use it the same way
Make the role owner for the service account (Member name = service account ID = service account email ex: thomasapp#appname-201813.iam.gserviceaccount.com
Copy the email address of your service account = service account ID
Simply go in your browser to the Google sheet you want to interact with
Go to SHARE on the top right of your screen
Go to advanced settings and share it with an email address of your service account ex: thomasapp#appname-201813.iam.gserviceaccount.com
I know it is a little late to answer but for other people struggling with the same issue.
Just change the permission of the sheet to public on your drive so it can be accessed without authentication via API calls.
To change access:
Open sheet in google drive
On top right corner, click share
On bottom of prompt window, click advanced
Change permission to public or people with link (no signin required)
Send API request to fetch data from sheets without authentication.
Note: if the sheet contains sensitive data then it is not safe to make it public and rather do it with Authenticated access.
Make sure to pay attention to #KishanPatel's comment:
Also, you can share this sheet with specific email Ex. your service
account (project) email. "client_email":
"XXXXX#northern-gasket-XXXX.iam.gserviceaccount.com", This will allow
to access sheet by your script.
Visual Simplification of the Answers:
Option 1 - Turn the file into public (if sheets the sheet contains sensitive data)
Option 2 - Share file with Service Account Email (IAM & Admin -> Service Accounts -> Details -> Email)
The easiest way is to fix using gcloud cli. More docs here https://cloud.google.com/pubsub/docs/quickstart-cli#before-you-begin
install gcloud
sudo apt-get install google-cloud-sdk
then call
gcloud init
then check your active project and credentials
gcloud config configurations list
If it is not ok, make sure you are authenticated with the correct account:
gcloud auth list
* account 1
account 2
Change to the project's account if not:
gcloud config set account `ACCOUNT`
Depending on the account, the project list will be different:
gcloud projects list
- project 1
- project 2...
Switch to intended project:
gcloud config set project `PROJECT NAME`
Then Create Application Default Credentials with gcloud auth application-default login, and then google-cloud will automatically detect such credentials.
My 10 cents... A simple example to read the sheet using Java.
private Credential getCredentials() throws IOException {
final InputStream accessKey = new ByteArrayInputStream("<credential json>");
final GoogleCredential credential = GoogleCredential.fromStream(accessKey)
.createScoped(Collections.singleton(SheetsScopes.SPREADSHEETS_READONLY));
return credential;
}
private HttpTransport httpTransport() {
try {
return GoogleNetHttpTransport.newTrustedTransport();
} catch (GeneralSecurityException | IOException e) {
throw new SpreadSheetServiceException(e);
}
}
Sheets service = new Sheets.Builder(httpTransport(), JSON_FACTORY, getCredentials())
.setApplicationName("app-name")
.build();
ValueRange response = service.spreadsheets().values()
.get("<spread_sheet_id>", "A1:A")
.execute();
In my case, solving this problem turned out to be trivial. You just have to:
Enter the google sheet that we want to remotely edit.
In the upper right corner, set - anyone who has the link can enter
Most importantly - on the right side you need to set permissions for people who have the link as 'editor'
if you still do not have permission, it means that you have to go to the website:
https://console.developers.google.com/iam-admin/iam/ then select your project, then select "Service accounts" and create a new one as role "owner" or" editor" for the project for example (or use one that already exists and click "create new key")
The "key" is a json file that will be downloaded when you create the account (or use "create new key" there).

How can I have a service account impersonate another service account? (Node.js)

I authenticate by setting the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path to a key for my App Engine service account (e.g. example#appspot.gserviceaccount.com).
If a calendar is shared directly to the App Engine service account, I can do just do this:
let googleCalendar = google.calendar({
version: 'v3',
auth: new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/calendar'],
})
});
But I want users to share their calendar with a different service account I have, google-calendar#example.iam.gserviceaccount.com. So I want the App Engine service account to impersonate the google-calendar account. I have tried this (and a few other minor variations):
let googleCalendar = google.calendar({
version: 'v3',
auth: new google.auth.GoogleAuth({
clientOptions: {
subject: 'google-calendar#uniserval-app.iam.gserviceaccount.com'
},
scopes: ['https://www.googleapis.com/auth/calendar'],
})
});
I get a 401 error: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
I tried making my App Engine service account a Member on the google-calendar service account and granting it various Roles such as Service Account User and Service Account Token Creator - but nothing changes.
I suspect the code is right and I just don't have the right Roles configured... but at this point I've been searching how to do this for 2 days and I can't find any documentation on exactly how to do this.
Your syntax looks correct, just that you appear to have misunderstood which email address to set as the subject. The subject is the user account on whose behalf you wish to act.
new google.auth.GoogleAuth({
clientOptions: {
subject: 'someone#yourdomain.com'
},
scopes: ['https://www.googleapis.com/auth/calendar'],
})

Accessing users account with service account

We need to use an API to verify if a certain user exists as managed account (it means, that belongs to our Google Domain organization).
GSuite adminSDK performs that operation, however, it requires OAuth2 authentication, authorized by an authenticated user - https://developers.google.com/admin-sdk/reports/v1/guides/authorizing .
My question is, if there any way to use that API with service account, or any other methos to retrieve this information with service account, since it would be used in a Server-2-Server scenario.
Thanks,
Vasco
As you may know, Service Accounts don't belong to an individual end user, but to an application. An administrator of a G Suite domain, though, can authorize the Service Account to access user data, that is, to impersonate users in the domain. This is called domain-wide delegation.
To achieve this, go to the Admin console and follow the steps specified here.
Reference:
Delegating domain-wide authority to the service account
Reports API > Perform G Suite Domain-Wide Delegation of Authority
Since this is completely not obvious - the docs "hint" at this, but don't spell it out and I had trouble finding any specific examples that worked. They all kept returning this error:
Google.GoogleApiException: 'Google.Apis.Requests.RequestError
Not Authorized to access this resource/api [403]
Errors [
Message[Not Authorized to access this resource/api] Location[ - ] Reason[forbidden] Domain[global]
]
The basic issue that the service account MUST impersonate another user. It's mentioned in this link at the bottom, highlighted in blue:
https://developers.google.com/admin-sdk/directory/v1/guides/delegation
Only users with access to the Admin APIs can access the Admin SDK Directory API, therefore your service account needs to impersonate one of those users to access the Admin SDK Directory API. Additionally, the user must have logged in at least once and accepted the Google Workspace Terms of Service.
But it just wasn't clicking as to how I was supposed to do that - was this some setting hiding in one of the admin consoles? No - you pass this as part of your initial connection.
So just to put the instructions in one place and hopefully save someone else the same headache, these were the steps:
Created a project via https://console.developers.google.com
Searched for then enabled the Admin SDK API
Created a Service Account
Show domain-wide delegation / Enable G Suite Domain-wide Delegation
At this point I had a service account name, unique ID, Email, and Client ID
It generated a key file (json) that I downloaded.
Go to: https://admin.google.com
Security > API Controls.
Manage Domain Wide Delegation
Added entry using client ID from above, applied these two scopes - you can apply other scopes as needed.
https://www.googleapis.com/auth/admin.directory.user.readonly
https://www.googleapis.com/auth/admin.directory.group.member.readonly
Then I created a .NET Core console app and installed these NuGet packages:
Google.Apis
Google.Apis.Auth
Google.Apis.Admin.Directory.directory_v1
Here's an ugly proof of concept with everything working:
using System;
using System.IO;
using System.Net;
using Google.Apis.Admin.Directory.directory_v1;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
namespace GoogleDirectoryTest
{
class Program
{
static void Main(string[] args)
{
var dirService = GetDirectoryService(#"C:\tmp\google-cred-sample-12345abdef.json", "user-with-admin-permission-here#mydomainhere.com", "My App Name");
var list = dirService.Users.List();
list.Domain = "mydomainhere.com";
var users = list.Execute();
foreach (var user in users.UsersValue)
{
Console.WriteLine($"{user.Name.FullName}");
}
Console.ReadKey();
}
static DirectoryService GetDirectoryService(string keyfilepath, string impersonateAccount, string appName)
{
using (var stream = new FileStream(keyfilepath, FileMode.Open, FileAccess.Read))
{
var credentials = GoogleCredential.FromStream(stream).CreateWithUser(impersonateAccount);
if (credentials.IsCreateScopedRequired)
credentials = credentials.CreateScoped(new[] { DirectoryService.Scope.AdminDirectoryUserReadonly });
var service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = appName,
});
return service;
}
}
Hopefully this saves someone else some headaches.
A service account is a special kind of account used by an application, rather than a person.
You can use a service account to access data or perform actions by the robot account itself, or to access data on behalf of Google Workspace or Cloud Identity users.
Prerequisites:
A Google Cloud Platform project
With the Admin SDK API enabled service account with domain-wide delegation.
A Google Workspace domain.
With account in that domain with administrator privileges.
Visual Studio 2013 or later
Step 1: Set up the Google Cloud Platform project
Create Google Cloud project
A Google Cloud project is required to use Google Workspace APIs and build Google Workspace add-ons or apps.
If you don't already have a Google Cloud project, refer to: How to create a Google Cloud project
Enable Google Workspace APIs
Before using Google APIs, you need to enable them in a Google Cloud project.
To Enable Google Workspace APIs refer to: How to Enable Google Workspace APIs
For this example you are enabling the the Admin SDK Directory API
with the data scope /auth/admin.directory.user.readonly.
Create Service Account with domain-wide delegation
To create service account refer to: How to create service account?
In the Domain wide delegation pane, select Manage Domain Wide Delegation.
Download Service Account private key (p12 format)
Download p12 file contains the private key for your Service Account.
Step 2: Set up the Google Workspace
Enable API access in the Google Workspace domain with
To enable API access in Google Workspace domain, refer to: how to enable API access
Delegating domain-wide authority to the service account
To call APIs on behalf of users in a Google Workspace organization, your service account needs to be granted domain-wide delegation of authority in the Google Workspace Admin console by a super administrator account
To delegating domain-wide authority in Google Workspace domain, refer to: How to Delegating domain-wide authority to the service account
Step 3: Prepare Visual Stodio project -
Create a new Visual C# Console Application (.NET Framework) project in Visual Studio.
Open the NuGet Package Manager Console, select the package source nuget.org, and run the following commands:
Install-Package Google.Apis.Auth
Install-Package Google.Apis.Admin.Directory.directory_v1
Step 4: Add code
Full example at GitHub
List top 10 users alias from Google Workspace Domain
/// <summary>
/// Example how to list all users from google workspace domain, using a service account (user impersonation).
/// </summary>
internal class Program {
static void Main(string[] args) {
// Scope for only retrieving users or user aliases.
string[] _scopes = {
"https://www.googleapis.com/auth/admin.directory.user.readonly"
};
var _paramters = new SACInitializeParameters(
// The service account ID (typically an e-mail address like: *#*iam.gserviceaccount.com)
serviceAccountId: "[Service Account ID]",
// The full path; name of a certificate file
x509CertificateFilePath: "[X509 Certificate File]",
// The email address of the user you trying to impersonate
impersonateEmail: "[User Email]",
// The scopes which indicate API access your application is requesting
scopes: _scopes);
using (var directoryService = DirectoryServiceFactory.CreateDirectoryService(_paramters)) {
// Retrieves a paginated list of either deleted users or all users in a domain.
var request = directoryService.Users.List();
// The unique ID for the customer's Google Workspace account
// the `my_customer` alias represent current identety account's
request.Customer = "my_customer";
request.MaxResults = 10;
var response = request.Execute();
foreach (var user in response.UsersValue) {
System.Console.WriteLine($"{user.Name.FullName}, {user.PrimaryEmail}, {user.Id}");
}
}
}
}

As a reseller, how do I create a new Google Apps user using the Admin SDK Directory API?

As we transition from the deprecated Provisioning API over to the new Directory API, I see some gaping holes in the documentation in regards to how I as a reseller can manage my customers (Google Apps customers). Specifically, I'd like to know how I can use my Reseller OAUTH credentials to create a new user on behalf of one of my customers (as was possible in the Provisioning API) using the following endpoint:
https://developers.google.com/admin-sdk/directory/v1/reference/users/insert
In the above documentation, I don't see a reference to a customer's account. The only parameters that I believe may be useful are organizations and externalIds. There's little documentation as to what these do. I was thinking of testing the following scenarios:
$params = array(
'externalIds' => array(
array(
'type' => 'customer', // or 'account'?
'value' => $data['domain']
)
),
'organizations' => array(
array(
'name' => $data['domain'],
'domain' => $data['domain'],
'type' => 'domain_only',
'primary' => true
)
)
);
Likewise, when performing operations on an existing user such as retrieval of a customer, I don't see where I can specify which Google Apps account the specific user is tied to:
https://developers.google.com/admin-sdk/directory/v1/reference/users/get
In this case, there's not even parameters for organization or externalIds, so I have no idea how retrieval could be specific to a particular customer under my reseller account.
I need to be able to perform these actions as I was able to in the old Provisioning API on behalf of my customers using only my Reseller account OAUTH credentials.
Any help would be appreciated.
Let's say my reseller account is reseller.com, and my resold domain is resold.com. In order to create a user on behalf of my resold domain, here is the call that I will make using my own reseller credential.
(https://developers.google.com/admin-sdk/directory/v1/guides/manage-users?hl=ja#create_user)
POST /admin/directory/v1/users HTTP/1.1
{
"name": {
"familyName": "Lam",
"givenName": "Emily"
},
"password": "anythingyouwant",
"primaryEmail": "emily#resold.com"
}
What you input in the primaryEmail field is where the user will be created. If you take a look at your resold domain admin console now, you will see that a user is now created.
Now again for retrieval, it is the same deal.
GET /admin/directory/v1/users/emily#resold.com
You will create these users and retrieve these users using your reseller credential as if you are the super admin of their domains. Here is the catch....
If the "Enable API access" is not manually checked in your resold domain Admin console (by default, this is now automatically checked for all newly resold domains at the moment), you can't make calls on behalf of the resold domains.
The button is in Admin Console -> Security -> API reference -> Enable API access