Google service account can't impersonate GSuite user - python

from google.oauth2 import service_account
import googleapiclient.discovery
SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = 'GOOGLE_SECRET.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject('address#example.com')
google_calendar = googleapiclient.discovery.build('calendar', 'v3', credentials=delegated_credentials)
events = google_calendar.events().list(
calendarId='address#example.com',
maxResults=10
).execute()
The result of the foregoing code is:
google.auth.exceptions.RefreshError: ('unauthorized_client: Client is unauthorized to retrieve access tokens using this method.', '{\n "error" : "unauthorized_client",\n "error_description" : "Client is unauthorized to retrieve access tokens using this method."\n}')
Domain-wide delegation is on. The service account client ID is authorized with the appropriate scopes in GSuite.
The service account works for normal credentials. It only doesn't work for delegated credentials.
I've tried different APIs (scopes) and different users in our domain.
I had a colleague try to code up a sample from scratch and he got the same thing.

I think you're problem is that your not authorising your credentials before making the call to the Calendar API but there is a couple of differences that you have that I use in my own implementation
Use the following import statements
from oauth2client.service_account import ServiceAccountCredentials
from apiclient import discovery
Use the from_json_keyfile_name method
Authorise the credentials and pass it as a http argument when building the service
Try this modification to your code:
from apiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials
import httplib2
SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = 'GOOGLE_SECRET.json'
credentials = ServiceAccountCredentials.from_json_keyfile_name(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
# Use the create_delegated() method and authorize the delegated credentials
delegated_credentials = credentials.create_delegated('address#example.com')
delegated_http = delegated_credentials.authorize(httplib2.Http())
google_calendar = discovery.build('calendar', 'v3', http=delegated_http)
events = google_calendar.events().list(
calendarId='address#example.com',
maxResults=10
).execute()
I would also suggest setting calendarId='primary' in your API call so that you always return the primary calendar of the user who you currently have delegated credentials for.
For more information on authenticating using ServiceAccountCredentials see the following link: http://oauth2client.readthedocs.io/en/latest/source/oauth2client.service_account.html

Related

Using Google Speadsheet API

[Updated]
I am trying to use Google Spreadsheet API to automate our reporting process (which is being handled manually). I have created Service Account and downloaded a json file.
from __future__ import print_function
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
def create(title):
"""
Creates the Sheet the user has access to.
Load pre-authorized user credentials from the environment.
TODO(developer) - See https://developers.google.com/identity
for guides on implementing OAuth2 for the application.
"""
creds, _ = google.auth.default()
# pylint: disable=maybe-no-member
try:
service = build('sheets', 'v4', credentials=creds)
spreadsheet = {
'properties': {
'title': title
}
}
spreadsheet = service.spreadsheets().create(body=spreadsheet,
fields='spreadsheetId') \
.execute()
print(f"Spreadsheet ID: {(spreadsheet.get('spreadsheetId'))}")
return spreadsheet.get('spreadsheetId')
except HttpError as error:
print(f"An error occurred: {error}")
return error
if __name__ == '__main__':
# Pass: title
create("mysheet1")
Here is the result:
TransportError: ("Failed to retrieve http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/?recursive=true from the Google Compute Enginemetadata service. Status: 404 Response:\nb''", <google_auth_httplib2._Response object at 0x7f262a34fd90>)
It's strange that there is no code lines to receive access to Google Work Space (like connect to API using keys?)
AFTER SETTING UP OAUTH CLIENTS ID
Now I am facing new problem. After running the code, It asked me to "Please visit this URL to authorize this application: with a link", I clicked and got this message: "This site can’t be reachedlocalhost refused to connect".(I am running the code on Colab)
Have you enabled the spreadsheet API ?
https://support.google.com/googleapi/answer/6158841?hl=en
I suspect it's because you have not given it the scope:
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly']
def main():
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
service = build('sheets', 'v4', credentials=creds)
Where credentials.json is your file
Scopes: https://developers.google.com/identity/protocols/oauth2/scopes#sheets
You will also need to download the gcloud cli:
If you ever try to run it locally, on your windows/linux/mac machine.
default credentials are the credentials stored by either the default service account in google cloud or your local machine
https://cloud.google.com/sdk/gcloud
And run
gcloud auth login

Google API using service account returning key error

I'm not sure what I am doing wrong here, the code here is basically taken from the docs. When running this code however I get a key error: access_token:
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user'
'https://www.googleapis.com/auth/admin.directory.group.member',
'https://www.googleapis.com/auth/admin.directory.group.member.readonly',
'https://www.googleapis.com/auth/admin.directory.group.readonly']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'keyfilecredentials.json',
scopes=SCOPES)
service = build('admin', 'directory_v1', credentials=credentials)
list = service.users().list().execute()

Google drive push notifications from entire organizations drive

I'm writing application that is supposed to scan each uploaded/modified file that meets some naming criteria. I need to set up a notification channel for entire drive of my organization, even users private directories. I found this resource but it requires authentication token for user (I suppose it's user that resources are being watched) https://developers.google.com/drive/api/v3/push
I have service account that I currently use to mock users I want to interact as https://developers.google.com/identity/protocols/oauth2/service-account
from google.oauth2 import service_account
from googleapiclient.discovery import build
dir_path = os.path.dirname(os.path.realpath(__file__))
SCOPES = [
'https://www.googleapis.com/auth/drive'
]
SERVICE_ACCOUNT_FILE = dir_path + '/../credentials.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject('userMail#org.com')
service = build('drive', 'v3', credentials=delegated_credentials)
That's how I currently access drive as a user I'd like to check. Is there any way to use that newly created service with delegated credentials to get authentication token required for drive push api?

Creating a webapp for managing Gmail email signatures for all employees from one place

I'm trying to create an application which allows the HR team to manage signatures for all the employees. Right now they use Gsuite to manage all the Gmail accounts of the employees. I'm looking for something which is similar to Signature Satori
I've tried using Gmail API to access the profile settings of a user and change the email signature of that authorized user. as shown [here]. Using the following code I've changed the signature on my gmail. (https://developers.google.com/gmail/api/guides/alias_and_signature_settings)
from __future__ import print_function
from apiclient import discovery
from httplib2 import Http
from oauth2client import file,client,tools
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
SCOPES = 'https://www.googleapis.com/auth/gmail.settings.basic'
creds = None
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
flow =
client.flow_from_clientsecrets('client_secret.json',SCOPES)
creds = tools.run_flow(flow,store)
GMAIL = discovery.build('gmail','v1',http=creds.authorize(Http()))
addresses =
GMAIL.users().settings().sendAs().list(userId='me')
.execute().get('sendAs',[])
for address in addresses:
if address['isPrimary']:
break
print(addresses)
def signature():
return 'hhello' + str(address['sendAsEmail'])
DATA = {'signature': signature()}
rsp =
GMAIL.users().settings().sendAs()
.patch(userId='me',sendAsEmail=address['sendAsEmail'],body=DATA)
.execute()
print(f'Primary address signature changed to {rsp['signature']}')
I want to do this and provide a template for all the users in the company managed from Gsuite. Any ideas of how to approach this problem?
You have to use a Service Account for this. According to Google's Gmail API Documentation:
Addresses other than the primary address for the account can only be
updated by service account clients that have been delegated
domain-wide authority.
You can create a service account on the Google Cloud Platform Console, the steps for setting one up can be found on the G Suite Admin Help pages here.
Once you have set up a service account, you must give it domain-wide delegation access so that you are able to gain access to the user accounts and edit their signatures on their behalf.
You can use the Users.settings.sendAs.update method of the Gmail API to update the signatures of each user by using the signature parameter of the Users.settings.sendAs resource:
{
"signature": "html-formatted-string"
}
You can now connect to the Users.settings.sendAs.update() resource, though you need to make sure you authenticate with your service account:
from __future__ import print_function
from googleapiclient.discovery import build
from apiclient import errors
from httplib2 import Http
from email.mime.text import MIMEText
import base64
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/gmail.settings.basic']
# This is your credentials file you downloaded from the Platform Console.
credsFile = 'service-key.json'
def signatureUpdater():
#...
emailAddresses = ["Populate", "an", "array", "with", "your", "user", "email", "addresses", "using", "Users: list", "method", "of", "Admin", "SDK", "API"]
q = {"signature": "The Signature You want your users to have <b>can include some html</b>"}
for x in emailAddresses:
response = serviceAccountLogin(x).users().settings().sendAs().update(userId = x, q = q).execute()
def serviceAccountLogin(email):
credentials = service_account.Credentials.from_service_account_file(credsFile, scopes = SCOPES)
delegatedCreds = credentials.with_subject(email)
service = build('gmail', 'v1', credentials = delegatedCreds)
return service
def main():
signatureUpdater()

How to get a list of most popular pages from Google Analytics in Python (Django)?

I'm trying to access Google Analytics API using the code provided by their documentation: https://developers.google.com/analytics/solutions/articles/hello-analytics-api
import httplib2
import os
from apiclient.discovery import build
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import run
CLIENT_SECRETS = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'client_secrets.json')
MISSING_CLIENT_SECRETS_MESSAGE = '%s is missing' % CLIENT_SECRETS
FLOW = flow_from_clientsecrets('%s' % CLIENT_SECRETS,
scope='https://www.googleapis.com/auth/analytics.readonly',
message=MISSING_CLIENT_SECRETS_MESSAGE,
)
TOKEN_FILE_NAME = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'analytics.dat')
def prepare_credentials():
storage = Storage(TOKEN_FILE_NAME)
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run(FLOW, storage)
return credentials
def initialize_service():
http = httplib2.Http()
credentials = prepare_credentials()
http = credentials.authorize(http)
return build('analytics', 'v3', http=http)
def get_analytics():
initialize_service()
But the problem is that this code opens a browser and asks a user to allow access to analytics service. Does anyone know how to access Google Analytics API (=obtain that token) without oauth2?
There are several ways to authorize Google APIs. You are using Web Server mode that allows you to access Google Services on behalf of your client, but what you really want to use in this case are Service Accounts.
First thing make sure the Analytics API Service is enabled for you Cloud Project in the Google Cloud Console.
Then go into Google Cloud Console and create a new Service Account Client ID. This will give you a certificate file and a Service Account Email. That's all you need.
Here's an example of how to authenticate and instantiate the Analytics API.
import httplib2
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
# Email of the Service Account.
SERVICE_ACCOUNT_EMAIL = '<some-id>#developer.gserviceaccount.com'
# Path to the Service Account's Private Key file.
SERVICE_ACCOUNT_PKCS12_FILE_PATH = '/path/to/<public_key_fingerprint>-privatekey.p12'
def createAnalyticsService():
f = file(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(SERVICE_ACCOUNT_EMAIL, key,
scope='https://www.googleapis.com/auth/analytics.readonly')
http = httplib2.Http()
http = credentials.authorize(http)
return build('analytics', 'v3', http=http)
This will access your Google Analytics Account as user SERVICE_ACCOUNT_EMAIL, so you have to go into Google Analytics and give this user access to your Analytics data.
Adapted from: https://developers.google.com/drive/web/service-accounts
As a result I've wrote a blog post with a solution that worked for me:
import httplib2
import os
import datetime
from django.conf import settings
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
# Email of the Service Account.
SERVICE_ACCOUNT_EMAIL ='12345#developer.gserviceaccount.com'
# Path to the Service Account's Private Key file.
SERVICE_ACCOUNT_PKCS12_FILE_PATH = os.path.join(
settings.BASE_DIR,
'53aa9f98bb0f8535c34e5cf59cee0f32de500c82-privatekey.p12',
)
def get_analytics():
f = file(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(
SERVICE_ACCOUNT_EMAIL,
key,
scope='https://www.googleapis.com/auth/analytics.readonly',
)
http = httplib2.Http()
http = credentials.authorize(http)
service = build('analytics', 'v3', http=http)
end_date = datetime.date.today()
# 30 days ago
start_date = end_date - datetime.timedelta(days=30)
data_query = service.data().ga().get(**{
'ids': 'ga:123456', # the code of our project in Google Analytics
'dimensions': 'ga:pageTitle,ga:pagePath',
'metrics': 'ga:pageviews,ga:uniquePageviews',
'start_date': start_date.strftime('%Y-%m-%d'),
'end_date': end_date.strftime('%Y-%m-%d'),
'sort': '-ga:pageviews',
})
analytics_data = data_query.execute()
return analytics_data

Categories

Resources