How to interact with the Gmail API - Delegate User - Python 3.7 - python-3.x

I am trying to write a Python application which simply adds a user as a delegate to another users mailbox.
I am following the API # Google API Documentation - Users.settings.delegates: create
However, I am struggling to find how to the parameters of:
User - an account which is TOBE added to a delegate Mailbox
Mailbox - the account which has the Mailbox I wish the account to become a delegate of.
I have currently tried making an API which has the delegate user. However, it does not seem to be interacting how I would expect. I am hoping Google will create a responsive API for the browser to support this. However, I am struggling with the code:
from googleapiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials
def main(user_to_be_added, delegated_mailbox):
service_account_credentials = ServiceAccountCredentials.from_json_keyfile_name('credentials/service_account.json')
service_account_credentials = service_account_credentials.create_scoped('https://mail.google.com/ https://www.googleapis.com/auth/gmail.insert https://www.googleapis.com/auth/gmail.modify')
service_account_credentials = service_account_credentials.create_delegated(user_to_be_added)
service = discovery.build('gmail', 'v1', credentials=service_account_credentials)
response = service.users().settings().delegates().create().execute(userId=delegated_mailbox)
if __name__ == '__main__':
main('some_account_to_be_added#gmail.com', 'delegated_mailbox#gmail.com')
Am I interacting with this API completely wrong? If so, how has anyone else achieved this?
Thank you for your time.
Jordan

Working Solution:
from googleapiclient import discovery
from google.oauth2 import service_account
def _create_client(subject):
credentials = service_account.Credentials
credentials = credentials.from_service_account_file('credentials/service_account.json',
scopes=['https://www.googleapis.com/auth/gmail.settings.sharing',
'https://www.googleapis.com/auth/gmail.settings.basic'],
subject=subject)
service = discovery.build('gmail', 'v1', credentials=credentials)
return service
def add_delegate_to_email(user_to_be_added, delegated_mailbox):
service = _create_client(user_to_be_added)
body = {
"delegateEmail": delegated_mailbox,
"verificationStatus": "accepted"
}
try:
response = service.users().settings().delegates().create(userId='me', body=body).execute()
print(response)
except Exception as e:
print('Exception: {}'.format(e))
Main problem: from oauth2client.service_account import ServiceAccountCredentials is deprecated as Google took ownership with google-auth.

Related

send mail using service account and google api python

Edit:
after accepting the answer:
my question was about a free Gmail account (I didn't know there was a difference) and the answer is about a paid account (and it is a currect one),
the answer showed me that there was a difference and it led me the correct answer to my situation - use a passcode
im trying to send mail using google API and service account, but I'm getting the following erre:
An error occurred: <HttpError 400 when requesting https://gmail.googleapis.com/gmail/v1/users/me/drafts?alt=json returned "Precondition check failed.". Details: "[{'message': 'Precondition check failed.', 'domain': 'global', 'reason': 'failedPrecondition'}]">
this is my code:
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.service_account import ServiceAccountCredentials
import base64
from email.message import EmailMessage
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
creds = ServiceAccountCredentials.from_json_keyfile_name(
"""path_to_cred_file.json""", SCOPES)
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'somemail#gmail.com'
message['From'] = 'somemail#gmail.com'
message['Subject'] = 'Automated draft'
# encoded message
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'message': {
'raw': encoded_message
}
}
# pylint: disable=E1101
draft = service.users().drafts().create(userId="me",
body=create_message).execute()
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
"Precondition check failed" usually means that you're trying to do something that cannot be done. In this case you're trying to send an email from a service account, which is not possible. This answer has a link to a relevant thread from the Google documentation. They say the following:
Service accounts dont work with gmail unless you set up domain wide delegation to a Gsuite account. The reason being is that a service account is its own user you need to delegate its permission to access your gmail account. This will only work with a gsuite domain email address.
This means that the service account by itself cannot send messages, but instead needs to be delegated access to a regular user account in order to send emails. To do this you can add the following line after your creds:
delegated_creds=credentials.with_subject("someuser#yourdomain.com")
#where someuser# is the email of the user that you're sending email as
After that you can use delegated_creds instead of creds to call the service.
Also, you seem to have gotten your sample from Google's guide, but note that your sample creates a draft instead of sending an email. The API call to send emails is a little different. With that in mind here's a complete example based on your code which worked for me:
#all the imports
SCOPES = ['https://mail.google.com/']
def main():
creds = None
creds = ServiceAccountCredentials.from_json_keyfile_name(
"""path_to_cred_file.json""", SCOPES)
delegated_creds=credentials.with_subject("someuser#yourdomain.com")
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=delegated_creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'somemail#gmail.com'
message['From'] = 'somemail#gmail.com'
message['Subject'] = 'Automated draft'
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'raw': encoded_message
}
email = service.users().messages().send(userId="me",
body=create_message).execute()
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
Finally, as explained in the thread I linked, this only works for Google Workspace accounts and you cannot delegate access to free Gmail accounts, so keep that in mind.

Make requests to Google API with Python

I'm trying to make requests to the Google API to create source repositories using a service account and his JSON key file.
Since there are no client libraries for this product, I am using the queries with Python using this documentation
https://cloud.google.com/source-repositories/docs/reference/rest
I already used a similar code to invoke my cloud-functions with success, but this time I'm block for these requests at the 401 error. I set up the GOOGLE_APPLICATION_CREDENTIALS with the JSON of my service account, give the service-account the permissions of Source Repository Administrator, but still return 401.
Here's my code
import urllib.request
import json
import urllib
import google.auth.transport.requests
import google.oauth2.id_token
body = { "name" : "projects/$my_project_name/repos/$name_repo"}
jsondata = json.dumps(body).encode("utf8")
req = urllib.request.Request('https://sourcerepo.googleapis.com/v1/projects/$my_project_name/repos')
req.add_header('Content-Type', 'application/json; charset=utf-8')
auth_req = google.auth.transport.requests.Request()
id_token = google.oauth2.id_token.fetch_id_token(auth_req, 'https://www.googleapis.com/auth/cloud-platform')
req.add_header("Authorization", f"Bearer {id_token}")
response = urllib.request.urlopen(req, jsondata)
print (response.read().decode())
I tried also using the with an API-KEY at the end of the url like this
req = urllib.request.Request('https://sourcerepo.googleapis.com/v1/projects/$my_project_name/repos?key=$my-api-key')
Thank you
I tried also using the with an API-KEY at the end of the url like this
API Keys are not supported.
Your code is using an OIDC Identity Token instead of an OAuth Acess Token.
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file(
'/path/to/key.json',
scopes=['https://www.googleapis.com/auth/cloud-platform'])
request = google.auth.transport.requests.Request()
credentials.refresh(request)
// Use the following code to add the access token:
req.add_header("Authorization", f"Bearer {credentials.token}")

Create an event with conference using python and Google Calendar API creates the event but not the conference

I am trying to create an event using Google Calendar API in Python 3. I also want to generate a Google Meet conference link for the event. I am using the documentations provided here:
https://developers.google.com/calendar/quickstart/python
https://developers.google.com/calendar/v3/reference/events#conferenceData
https://developers.google.com/calendar/create-events
The event is created without a problem. However, it is missing the conference link. My code so far is as follows:
from pathlib import Path
from pickle import load
from pickle import dump
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from uuid import uuid4
from typing import Dict, List
from oauth2client import file, client, tools
class EventPlanner:
def __init__(self, guests: Dict[str, str], schedule: Dict[str, str]):
guests = [{"email": email} for email in guests.values()]
service = self._authorize()
self.event_states = self._plan_event(guests, schedule, service)
#staticmethod
def _authorize():
scopes = ["https://www.googleapis.com/auth/calendar"]
credentials = None
token_file = Path("./calendar_creds/token.pickle")
if token_file.exists():
with open(token_file, "rb") as token:
credentials = load(token)
if not credentials or not credentials.valid:
if credentials and credentials.expired and credentials.refresh_token:
credentials.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('calendar_creds/credentials.json', scopes)
credentials = flow.run_local_server(port=0)
with open(token_file, "wb") as token:
dump(credentials, token)
calendar_service = build("calendar", "v3", credentials=credentials)
return calendar_service
#staticmethod
def _plan_event(attendees: List[Dict[str, str]], event_time, service: build):
event = {"summary": "test meeting",
"start": {"dateTime": event_time["start"]},
"end": {"dateTime": event_time["end"]},
"attendees": attendees,
"conferenceData": {"createRequest": {"requestId": f"{uuid4().hex}",
"conferenceSolutionKey": {"type": "hangoutsMeet"}}},
"reminders": {"useDefault": True}
}
event = service.events().insert(calendarId="primary", sendNotifications=True, body=event, conferenceDataVersion=1).execute()
return event
if __name__ == "__main__":
plan = EventPlanner({"test_guest": "test.guest#gmail.com"}, {"start": "2020-07-31T16:00:00",
"end": "2020-07-31T16:30:00"})
print(plan.event_states)
I suspect that the problem is with where I have passed conferenceDataVersion but the docs are not exactly clear about where it has to be passed other than that it must be passed. I also tried putting it in the body of the event or in createRequest. It always creates the event but not the conference. Unfortunately, I could not find anything about this online anywhere. Maybe I'm actually that bad at searching, but I have been testing different things and searching for a solution for several days! If anyone knows what I am missing, I will truly appreciate their assistance.
Thanks to #Tanaike, I found what was the problem. The token which is generated the first time the API is authenticated is very specific. The problem I was having turned out to be just with that. As soon as I removed the token and had it get generated again, the problem was solved. That being said, I have no idea why the problem appeared in the first place. I will update the response if I find the reason behind it. But for now, if you are having the same problem, just remove the token and regenerate it.
With this code we can create the conference (Google meet link) for me it works
"conferenceData": {
"createRequest": {
"requestId": "SecureRandom.uuid"
}
}

Modifying a private calender with Python backend using google functions

so I have the following problem:
I want to manipulate a calendar(my private google calendar) using GoogleFunctions with python backend.I have now followed an code example (which inserts an event) and in the google console it is also shown that a call has taken place. Now I'm just wondering which calendar I'm modifying?
The problem for me is, as soon as I enter the calendarId of my private calendar, the function runs on an error. But actually the service user has all permissions. If I understand it correctly, then I can manipulate all calendars within the organization with the Service user?
So that's my code. It works so far. Now I only ask myself, can I see the calendar I am modifying somewhere?
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
SUBJECT = 's.........415.iam.gserviceaccount.com'
def main():
# create credntials
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject(SUBJECT)
service = build('calendar', 'v3', credentials=delegated_credentials)
d = datetime.utcnow().date()
tomorrow = datetime(d.year, d.month, d.day, 10)
start = tomorrow.isoformat()
end = (tomorrow).isoformat()
body={"summary": 'Hello there, Automating calendar',
"description": 'Google calendar with python',
"start": {"dateTime": start, "timeZone": 'Asia/Karachi'},
"end": {"dateTime": end, "timeZone": 'Asia/Karachi'},
}
event = service.events().insert(calendarId=SUBJECT,body=body).execute()
if __name__ == "__main__":
main()

Authorisation problems when using google service account. Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup

I am writing a python wrapper for fusion tables.
I decided to use google service account for accessing the service. My code is:
import httplib2
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
scopes = ['https://www.googleapis.com/auth/fusiontables']
credentials = ServiceAccountCredentials.from_json_keyfile_name('__Remak APIS-37c11e21531ad.json', scopes)
http = httplib2.Http()
if not credentials.access_token:
credentials.refresh(http)
service = build('fusiontables', 'v2', http=http)
def test():
table_id = '1esH-YayZegZH69VsiVBq0YK9hxgP-JWTCljRuQUZy'
print(service.query().sql(sql='SELECT * FROM {}'.format(table_id)).execute(http=http))
if __name__ == '__main__':
test()
the output is:
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/fusiontables/v2/query?alt=json&sql=SELECT+%2A+FROM+1esH-YayrtegZH6VsiVBq0YK9hxgP-JWTCljRuQUZy returned "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.">
I just enabled this API that is why the daily limit is for sure hasn't reached. I also tried to find the answer in similar topics, but I wasn't succeeded.
Thank you in advance
Finally made it work:
there were one part missed:
credentials.authorize(http)
Now it seems OK
The code is:
import httplib2
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
scopes = ['https://www.googleapis.com/auth/fusiontables']
KEY = '__Remak APIS-a8cd56ca2b4e.json'
credentials = ServiceAccountCredentials.from_json_keyfile_name(KEY, scopes)
http = httplib2.Http()
credentials.authorize(http)
if not credentials.access_token:
credentials.refresh(http)
fusiontables = build('fusiontables', 'v2', http=http)
def test():
table_id = '1esH-YayZegZH97VsiVBq0YK9hxgP-JWTCljRuQUZy'
print(fusiontables.query().sql(sql='SELECT * FROM {}'.format(table_id)).execute())
if __name__ == '__main__':
test()

Resources