I can't access the blank google spreadsheet i created using python - python

So i have been trying to create a new blank google spreadsheet using python. I am using a python script i found online (also added a few modifications of my own) and i just can't get it to work the way i want. I've never actually used python before, neither google spreadsheets so i am a little confused!! The current issue is that whenever i run the code it seems to be working, but when i copy/paste the URL of the newly generated google spreadsheet, i don't even have permission to view it. Here is my code....Thank you in advance!!
"""
BEFORE RUNNING:
---------------
1. If not already done, enable the Google Sheets API
and check the quota for your project at
https://console.developers.google.com/apis/api/sheets
2. Install the Python client library for Google APIs by running
`pip install --upgrade google-api-python-client`
"""
from pprint import pprint
from googleapiclient import discovery
import gspread
from oauth2client.service_account import ServiceAccountCredentials
from google.oauth2 import service_account
# TODO: Change placeholder below to generate authentication credentials. See
# https://developers.google.com/sheets/quickstart/python#step_3_set_up_the_sample
#
# Authorize using one of the following scopes:
# 'https://www.googleapis.com/auth/drive'
# 'https://www.googleapis.com/auth/drive.file'
# 'https://www.googleapis.com/auth/spreadsheets'
SERVICE_ACCOUNT_FILE = 'client_secret.json'
f = open('client_secret.json','r')
print(f.readline())
f.close()
SCOPES = ["https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive"]
credentials = None
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = discovery.build('sheets', 'v4', credentials=credentials)
spreadsheet_body = {
# TODO: Add desired entries to the request body.
}
request = service.spreadsheets().create(body=spreadsheet_body)
response = request.execute()
# TODO: Change code below to process the `response` dict:
pprint(response)

The issue is that the sheet is created with the serviceaccount as the owner, not under your personal Gmail account.
There's 2 options:
The bad way would be to give your personal account access to the generated GSheet. Issue with this is that the serviceaccount will still be the owner. I'm not going to tell you how to do this as this is absolutely the wrong way.
The right way would be to use proper credentials when creating the API client. This is explained in detail in this article.
Pay special attention to the piece of code that creates the credentials object.
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('sheets', 'v4', credentials=creds)
As you can see this code doesn't directly create a credentials object from the serviceacocunt. Instead it asks for permission to use your personal Gmail account to call the API.
Note that if you have a GSuite/Workspace account, you can use impersonation instead. This is actually the preferred way, but only works with said GSuite/Workspace accounts.

Related

Error 400: invalid_scope ["https://www.googleapis.com/auth/apps.reporting.audit"] unable to set up email monitor with googleAPI python client

I began following the code sample given on googleapis github page to help me understand how the Email audit API works.
The sample initialized the API service like this:
from googleapiclient import sample_tools
service, flags = sample_tools.init(
argv,
"audit",
"v1",
__doc__,
__file__,
scope="https://www.googleapis.com/auth/apps/reporting/audit.readonly",
)
Since for my purposes, I'll need read AND write permissions, I included the scope as 'https://www.googleapis.com/auth/apps/reporting/audit'
Here's how I am trying to initialize the service:
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
import os
SCOPES = [
'https://www.googleapis.com/auth/apps.reporting.audit'
]
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
#now attempting to initialize the audit service
auditService = build('audit', 'v1', credentials=creds)
Now, I am facing two issues here:
I can't access the given scope
After I am prompted to authorize the scopes by logging in to my admin account, I am shown the following message:
Authorization Error
Error 400:
invalid_scope
Some requested scopes cannot be shown: [https://www.googleapis.com/auth/apps.reporting.audit]
For testing, if I only request readonly scopes, I get:
googleapiclient.errors.UnknownApiNameOrVersion: name: audit version: v1
Can someone please guide me through how to properly set up an email monitor using googleapis python client? (Is the given sample on github outdated?)
The sample code mentioned actually refers to the (now deprecated) Enterprise Activity API. This service was moved to Reports API and as Enterprise Activity API, it is only available for Workspace domains.
So indeed, this script is outdated. If you want to use Reports API to manage Activities, you may want to refer to the python quickstart here.
The end goal of your script is unclear, however as you’ve mentioned you’d like to use Email Audit API, I’d recommend following this guide to confirm if this is the right API for your demand. Keep in mind that this API is also only available for Workspace Domains.
Alternatively, I’d also recommend having a look at GMail API capabilities to see if it fits your needs.

Any way to create a new Google Calendar event without token.pickle

Using token.pickle is quite frustrating, as every couple of weeks the token expires, and then I need to manually delete it from my source files in code, so it can regenerate itself, and then I need to re-authenticate it from my account.
Is there a way I can just create a new service without it? I know it's possible for Google sheets files. This is what that looks like:
def get_g_sheets_service():
SERVICE_ACCOUNT_FILE = 'key.json'
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
creds = None
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
SAMPLE_SPREADSHEET_ID = 'ID_GOES_HERE'
service = build('sheets', 'v4', credentials=creds)
return service
but, the way to get a service for the calendar API looks like this:
import datetime
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar']
CREDENTIALS_FILE = 'path_to_file/credentials.json'
def get_calendar_service():
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
CREDENTIALS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
return service
Notice the token.pickle file?
How can I not deal with it?
why is the token.pickle expring?
The token.pickle should contain an access token and a refresh token which was created with the user consented to your application accessing their data. Access tokens are used to access user data and expire after an hour, refresh tokens are use to request a new access token when it has expired. This is done automatically via the client library you are using. Refresh tokens for the most part should not be expiring see: experation.
You need to be sure you are always storing the most recent refresh token.
If your application is still in the testing phase refresh tokens will expire after seven days. see: experation
A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days.
The solution is to go to google cloud console under the oauth2 consent screen and set your application to production.
service accounts
If this google calendar is part of google workspace. then your workspace admin could grant a service account domain wide delegation and allow you to impersonate a user on the domain with the service account. This form for authorization is much easer and will not have the same expiration token issues as the authorization is configured via workspace.
Service accounts only work though google calendar with workspace domain accounts.

How to let my users save their configuration on Google Drive without sahring my developer credentials.json?

There is something I don't understand, when it comes to using the Google Drive API.
I'm trying to develop a desktop application that lets the user save his config file to his personal Google Drive, so he can use the same config from any computer.
The Python Quickstart guide has an example how to let a user authenticate, but this example requires the user to have the "credentials.json" file that I created in the Google Console. My understanding is that I should not share this file publicly.
So can I allow users to synchronize their configuration on multiple desktop computers without giving them the app's credentials?
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
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 = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
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.')
return
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
except HttpError as error:
# TODO(developer) - Handle errors from drive API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
You should consult the TOS for using the Google apis
Asking developers to make reasonable efforts to keep their private keys private and not embed them in open source projects.
If you are giving your users the copy of your python code. You may not give your users your credetinals.json file. You must instead instruct your users on how to create their own credetinals.json file.
Solution
Solution to not sharing your credentials its to teach the users of your application to create their own credentials.
I have 1 idea. Use selenium and automate process of creating credentials.json. In this way non-technical users not need to do anything and with this automatic process credentials.json shall be created using users google account.
Apply this idea and you shall see good results.

Google Apps Script API - Permissions for my Python script to execute an Apps Script function

this is my first contribution here.
I'm trying to access Gmail through a python script. To do so, I've created a Google Apps Script function and used the Apps Script API between the 2.
(This doc displays what I'm trying to do)
So the python script correctly accesses the API, but fails to execute the function.
While it works in the Script Editor, in Python it raises a permissions issue:
'errorMessage': 'Exception: The script does not have permission to perform that action.
Required permissions: (
https://www.googleapis.com/auth/gmail.labels ||
https://www.googleapis.com/auth/gmail.metadata ||
https://www.googleapis.com/auth/gmail.readonly ||
https://www.googleapis.com/auth/gmail.modify ||
https://mail.google.com/
)',
'errorType': 'ScriptError'
I guess it is related to the Client ID OAuth, since I was not able to find where to grant it permissions. I've just :
created the credentials in Google Cloud Platform,
exported it as creds.json in my python script folder.
Here is my code, almost copy pasted from a tutorial:
import pickle
import os.path
from googleapiclient import errors
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# Here I've edited the scopes of authorizations required
SCOPES = [
"https://www.googleapis.com/auth/gmail.labels",
"https://www.googleapis.com/auth/gmail.metadata",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.modify",
"https://mail.google.com/"
]
def get_scripts_service():
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
# Here I've placed the downloaded credentials .json file
flow = InstalledAppFlow.from_client_secrets_file(
'creds.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
return build('script', 'v1', credentials=creds)
service = get_scripts_service()
API_ID = # Here i've pasted my API_ID
request = {"function": # Here i've pasted my functionName}
try:
response = service.scripts().run(body=request, scriptId=API_ID).execute()
print (response)
except errors.HttpError as error:
# The API encountered a problem.
print(error.content)
How do I grant permissions to my script?
Simple as Aerials said! Thanks.
It was because the Client ID was created before I edited the scopes. I've deleted the token and created a new one.

How to give proper auth for a Google Cloud Function to access the Calendar API in Python

I have a script that checks a calendar for some events on a private calendar, and I want to have it run in GCF. Right now, I followed the Calendar API quickstart guide, and my authentication method is as follows:
# relevant imports
from apiclient.discovery import build
from oauth2client import file, client, tools
from httplib2 import Http
# Setup the Calendar API
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
store = file.Storage('token.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('calendar', 'v3', http=creds.authorize(Http()))
Here, token.json and credentials.json are from the configuration file after enabling the Calendar API on my project.
If I copy this directly to a function, I of course hit an error when it tried to load credentials.json, since there are no external files.
Given that my goal is to make this accessible from a GCF url, how can I make this authentication work?
Note: I do have a service account associated with the function, so is there a way I can give that SA access to the private calendar and somehow authenticate with that SA in the python script?

Categories

Resources