Google API Drive V3 retrieving drive storage space used - python

I'm using a google service account to retrieve the usage of data from different users.
I'm using google's python client to authenticate and retrieve the data.
Code
service = build('drive', 'v3', credentials=auth)
result = service.about().get(email).execute();
result = result.get("storageQuota", {})
I keep getting the following error:
method() takes 1 positional argument but 2 were given
I want to be able to get it from a specific user's drive information using the email as an identifier.

How to get Drive info from yourself
Try this snippet example:
result = service.about().get(fields="*").execute()
result = result.get("storageQuota", {})
print(result)
The print output is:
{'usage': '11638750', 'usageInDrive': '11638750', 'usageInDriveTrash': '7531862'}
How to get Drive info from user in your domain
If you are an admin and want to get users info, do the next steps:
Create project in Admin Console
Create service account
Go to Admin Console > Security > Advanced settings > Manage API client access
In Client Name put the full email of your created Service Account
In One or More API Scopes put https://www.googleapis.com/auth/drive and click Authorize
Come back to Service accounts, select your account, Enable G Suite Domain-wide Delegation
Create Service Account KEY (download it as .json)
Activate Drive API for your project. Go to APIs & Services > Dashboard, click on ENABLE APIS AND SERVICES, search for Drive and Enable it.
Create index.py file with the next code and launch it:
from googleapiclient.discovery import build
from google.oauth2 import service_account
def main():
SCOPES = ['https://www.googleapis.com/auth/drive']
SERVICE_ACCOUNT_FILE = 'serviceaccountsproject-81ec0d3c1c1c.json'
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
credentials = credentials.with_subject('user#inYourDomain.com')
service = build('drive', 'v3', credentials=credentials)
result = service.about().get(fields="*").execute()
result = result.get("storageQuota", {})
print(result)
if __name__ == '__main__':
main()
And here is an output:
{'usage': '0', 'usageInDrive': '0', 'usageInDriveTrash': '0'}
Reference:
Google Drive API V3 - About - get
Understanding service accounts

The is an undocumented required paramater with that request. Its called fields. I have a bug report out.
service = build('drive', 'v3', credentials=auth)
driveRequest = service.about().get(email);
driveRequest.fields = "*";
result = driveRequest.execute();
result = result.get("storageQuota", {})
Kindly note i am not a python developer this is a guess on how to do it.

Related

No files found - print folders and file list using a service google account

I enabled the google drive api, created a service account, added the service account email to the folder and successfully generated the .json credentials
But when I try to print the contents of the temp2 folder I get "no files found"
I'm attempting to do it this way
import logging
from google.oauth2.service_account import Credentials
from googleapiclient.discovery import build
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
creds = Credentials.from_service_account_file('bayfiles-779393795f34.json', scopes=['https://www.googleapis.com/auth/drive'])
service = build('drive', 'v3', credentials=creds)
query = "mimeType='application/vnd.google-apps.folder' or mimeType='application/vnd.google-apps.document' or mimeType='application/vnd.google-apps.spreadsheet' or mimeType='application/vnd.google-apps.presentation' and trashed = false and parents in '16x7o7CNCscM-lFqnKaXwUf1Bv-OTQK0W'"
results = service.files().list(q=query).execute()
items = results.get("files", [])
if not items:
logger.debug("No files found")
else:
# Print file names
for item in items:
logger.debug(f'The service account has access to the file "{item["name"]}" with ID "{item["id"]}"')
The strange thing is that the number of requests appears to me in the API page of my project
but through the code it is returned to me as if there were no files inside the temp2 folder, but it's wrong.
I tested locally, with my user account and your filter and your code works well. But as I said, strangely.
In fact, only the presentation not trashed in the mentioned folder are returned + all the other file mentioned in the OR clauses. Again, add parenthesis like that to narrow only to the current mentioned folder
query = "(mimeType='application/vnd.google-apps.folder' or mimeType='application/vnd.google-apps.document' or mimeType='application/vnd.google-apps.spreadsheet' or mimeType='application/vnd.google-apps.presentation') and trashed = false and parents in '16x7o7CNCscM-lFqnKaXwUf1Bv-OTQK0W'"
Are you sure about the Folder ID?
Let me show you how to avoid a service account key file (which is a bad practice).
You have to scope correctly your user credential. Use that CLI command for that
gcloud auth application-default login \
--scopes="https://www.googleapis.com/auth/cloud-platform","https://www.googleapis.com/auth/drive"
Keep in mind that is now your user account that will be used, and YOUR email must be granted on the Google Drive folder
If you want to use the service account identity instead of your own user email, you can use impersonation
gcloud auth application-default login \
--scopes="https://www.googleapis.com/auth/cloud-platform","https://www.googleapis.com/auth/drive" \
--impersonate-service-account="<service account email>"
You must have the role "project owner" or "service account token creator" to be able to impersonate a service account. But like that, you don't need a secret and sensitive file (JSON)
Update your code. You will see 2 changes
import logging
#from google.oauth2.service_account import Credentials -- No longer need it
import google.auth #Required for getting the default authentication
from googleapiclient.discovery import build
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
#creds = Credentials.from_service_account_file('bayfiles-779393795f34.json', scopes=['https://www.googleapis.com/auth/drive'])
# Prefer the default credential instead. Like that it works locally and in the cloud the same way
# Scopes are optional locally, but required in the cloud runtime environment
credentials, project_id = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform","https://www.googleapis.com/auth/drive"])
# With your user credential you also have to notify explicitly the quota project. It's optional for cloud runtime environment
service = build('drive', 'v3', credentials=credentials,client_options={"quota_project_id":project_id})
query = "mimeType='application/vnd.google-apps.folder' or mimeType='application/vnd.google-apps.document' or mimeType='application/vnd.google-apps.spreadsheet' or mimeType='application/vnd.google-apps.presentation' and trashed = false and parents in '16x7o7CNCscM-lFqnKaXwUf1Bv-OTQK0W'"
results = service.files().list(q=query).execute()
items = results.get("files", [])
if not items:
logger.debug("No files found")
else:
# Print file names
for item in items:
logger.debug(f'The service account has access to the file "{item["name"]}" with ID "{item["id"]}"')

How to create user in Google Admin with Service Account (server side)

I am trying to create a user in Google Admin via API
(google admin)
Things I have done:
create service account and credentials
installed sdk lib
read multiple docs on service account/Oauth credentials, google sdk authen., admin api
code
class Gcp:
def __init__(self):
SERVICE_ACCOUNT_FILE = "core/gcp/XXX.json"
read_domain_scope = 'https://www.googleapis.com/auth/admin.directory.domain.readonly'
admin_user_scope = 'https://www.googleapis.com/auth/admin.directory.user'
read_ou_scope = 'https://www.googleapis.com/auth/admin.directory.orgunit.readonly'
SCOPES = [read_domain_scope, admin_user_scope, read_ou_scope,]
self.credential = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
def list_user(self):
res = googleapiclient.discovery.build('users', 'v1', credentials=self.credential)
return 'OK'
Problems:
I can not find a document which specify how I can create user in google admin via SDK, I mean I've found the REST api doc
But I seems that I need to authenticate through OAuth2 for the REST calls.
Is there a way to get this (create user and assign domain/organization unit) done with just service account credential file with SDK?

Python script for google drive is redirecting to chrome authentication

I write a python script to upload file to google drive, but the script is redirecting to chrome for email user authentication.
is there any way to avoid redirecting to chrome for authentication.
I'm running on python 3.9.
here is my sample code:
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
gauth = GoogleAuth()
drive = GoogleDrive(gauth)
upload_file_list = ['myfile.pdf']
for upload_file in upload_file_list:
gfile = drive.CreateFile({'parents': [{'id': '1B8ttlQMRUkjbrscevfa1DablIayzObh2'}]})
# Read file and set it as the content of this instance.
gfile.SetContentFile(upload_file)
gfile.Upload() # Upload the file.
The behaviour you are reporting is totally normal with OAuth 2.0 and the official Google APIs library.
What #Tanaike said is a good solution. You could use a service account to access Google Drive files without granting consent every time the token expires. With service accounts there are 2 options to achieve that:
Share the file/folder with the email address of the service account.
Use domain-wide delegation of authority to allow the service account to impersonate any user in your domain. Requires a domain using Google Workspace or Cloud Identity and Super Admin access to configure domain-wide delegation.
General information on how to make API calls with domain-wide delegation is available on this page https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests.
Here is a working code sample:
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# Scopes required by this endpoint
# https://developers.google.com/drive/api/v3/reference/permissions/list
SCOPES = ["https://www.googleapis.com/auth/drive.readonly"]
# Variable that holds the file ID
DOCUMENT_ID = "i0321LSy8mmkx_Bw-XlDyzQ_b3Ny9m74u"
# Service account Credential file downloaded with domain-wide delegation of authority
# or with shared access to the file.
SERVICE_ACCOUNT_FILE = "serviceaccount.json";
# Creation of the credentials
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE,
scopes=SCOPES)
# [Impersonation] the service account will take action on behalf of the user,
# requires domain-wide delegation of authority.
delegated_credentials = credentials.with_subject('user#domain.com')
# The API call is attempted
try:
service = build('drive', 'v3', credentials=delegated_credentials)
# Retrieve the documents contents from the Docs service.
document = service.files().get(fileId=DOCUMENT_ID).execute()
print('The title of the document is: {}'.format(document.get('name')))
except HttpError as err:
print(err)
Keep in mind that to use user impersonation you will need to configure domain-wide delegation in the Admin console of the domain that has the files (this will also work for external files shared with users in the domain).
If you want to use this with regular consumer accounts you can't use user impersonation, instead you will share the file with the service account (read or write access) to later make API calls. Line 20 creates delegated credentials, this line needs to be removed if you will use this other approach.

unable to connect to localhost after allowing permissions for the google app

I want to implement a simple application that would allow me to access Google Drive. I am following python quickstart. I run the application in the Docker.
However when I run the script it shows me Please visit this URL to authorize this application:. If I go by the URL it asks me to Choose Account, shows warning regarding it not being a verified app (I ignore it and go my app page), asks for access to google drive and metadata (I allow it), and then it redirects me to http://localhost:46159/?state=f.. and it shows unable to connect page. Port may differ.
What is the problem? Is there a way to prevent the application running in Docker to ask for verification?
In order to avoid the "asking for verification" process you can instead use authorisation through service accounts.
In order to do so, firstly we have to create the service account:
Navigate to your GCP project.
Go to Credentials
Click on Create credentials>Service account key
Set the service account name, ID and Role (if applicable). Leave the Key type as JSON.
Click on Create. A JSON file will be downloaded containing the credentials of the newly created Service account.
Now, copy the file to the folder holding your project and use the following modified code (based on the Quickstart example you used):
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2 import service_account
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
if __name__ == '__main__':
main()
Note that Service Accounts behave like normal accounts (they have their own files, permissions, etc.). If you want a service account to act like an existing user of your domain, you can manage to do so by using Domain-wide delegation.
Reference
Create a service account
Using OAuth 2.0 for Server to Server Applications
Domain-Wide Delegation of Authority

Google OAuth client is using the wrong project_id from the json file- Python

My Python (3.6.7) code uses oauth2client to access Google Photos APIs. It successfully authenticates, but when it tries to access the Google Photos albums, it seems to be using the username as the project_id.
from __future__ import print_function
from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools
# Setup the Photo v1 API
SCOPES = 'https://www.googleapis.com/auth/photoslibrary.readonly'
store = file.Storage('credentials.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('scripts/client_id.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('photoslibrary', 'v1', http=creds.authorize(Http()))
# Call the Photo v1 API
results = service.albums().list(
pageSize=10, fields="nextPageToken,albums(id,title)").execute()
items = results.get('albums', [])
if not items:
print('No albums found.')
else:
print('Albums:')
for item in items:
print('{0} ({1})'.format(item['title'].encode('utf8'), item['id']))
When executing the above code, it prompts me the auth page. When I successfully authenticate, it shows me the following error:
HttpError 403 when requesting {URL} returned "Photos Library API has not been used in project 123456 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/photoslibrary.googleapis.com/overview?project=123456 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.">
Interestingly, the number in bold 123456 (obviously changed) is actually the first part of the client_id found in the client_id.json
But the project_id looks something like this: test1-235515
So what I got from this error is that the oauth2client client is passing the client_id instead of the project_id. So even though I have enabled the Photos API, it will never access it correctly.
Please help with this error. How can I manually change the project_id?
The project ID is different from the project number. You will be able to see both in your Google Cloud Console configuration. See this documentation for more on how to identify your projects [1].
A single Google Cloud project can have many different OAuth client IDs configured. See this documentation for information about creating OAuth client credentials [2]. You should be only have to make sure that the client you created belongs to the project for which you have enabled APIs. Going to the URL provided in the error message should take you to the right configuration page.
[1] https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects
[2] https://support.google.com/cloud/answer/6158849?hl=en

Categories