Using Google API Python client with SignedJwtAssertionCredentials - python

I am trying to get a list of users from Google using google_api_python_client-1.4.0. I getting the access_denied error even through I have domain wide delegation authority.
Interesting thing is that.. when I use the same certificate/credentials with .net client library, it works.
The error I am getting is
File "/Library/Python/2.7/site-packages/oauth2client-1.4.6-py2.7.egg/oauth2client/client.py", line 807, in _do_refresh_request
oauth2client.client.AccessTokenRefreshError: access_denied: Requested client not authorized.
# Load the key in PKCS 12 format that you downloaded from the Google API
# Console when you created your Service account.
f = file('Gkeys/87ty8g87-privatekey.p12', 'rb')
key = f.read()
f.close()
# Create an httplib2.Http object to handle our HTTP requests and authorize it
# with the Credentials. Note that the first parameter, service_account_name,
# is the Email address created for the Service account. It must be the email
# address associated with the key that was created.
credentials = SignedJwtAssertionCredentials(
'889h98h98h98h98h9lurk#developer.gserviceaccount.com',
key,
scope=['https://www.googleapis.com/auth/admin.directory.group.readonly','https://www.googleapis.com/auth/admin.directory.user.readonly'],
private_key_password='notasecret',
sub='admin.user#domain.com'
)
http = httplib2.Http()
http = credentials.authorize(http)
directory_service = build('admin', 'directory_v1', http=http)
all_users = []
page_token = None
params = {'groupKey': 'groupname#domain.com'}
while True:
try:
if page_token:
params['pageToken'] = page_token
#current_page = directory_service.users().list(**params).execute()
#current_page = directory_service.members().list(**params).execute()
current_page = directory_service.members().list(groupKey='groupname#domain.com').execute()
all_users.extend(current_page['users'])
page_token = current_page.get('nextPageToken')
if not page_token:
break
except errors.HttpError as error:
print 'An error occurred: %s' % error
break
for user in all_users:
print user['primaryEmail']

Are you sure the scopes you authorized in the control panel exactly match those you're requesting here? If you authorized the read/write scope and are using the readonly scope here I believe that would cause your error.

Related

Getting 'HttpError 401' when attempting to use Google Drive API with delegated credentials

I'm attempting to look at the files for all the users in my org using the Google API in Python. I have a service account with domain-wide delegation. I'm attempting to create delegated credentials for each user so that I can look at their files.
However, when I run the code below, on this line in the for loop:
results = drive_service.files().list(
pageSize=10, fields="").execute()
I get this error:
googleapiclient.errors.HttpError: <HttpError 401 when requesting https://www.googleapis.com/drive/v3/files?pageSize=10&fields=nextPageToken%2C+files%28id%2C+name%29&alt=json returned "Invalid Credentials". Details: "[{'domain': 'global', 'reason': 'authError', 'message': 'Invalid Credentials', 'locationType': 'header', 'location': 'Authorization'}]">
The same line above that does not use delegated credentials works fine, (so I know I have the necessary scopes and the Drive API is enabled) so I think something is wrong with del_creds. I have triple checked that domain wide delegation is enabled. Any help is appreciated!
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user', 'https://www.googleapis.com/auth/admin.directory.user.security', 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive.readonly']
CREDS = 'service-account-credentials.json'
def main():
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.
creds = service_account.Credentials.from_service_account_file(
CREDS, scopes=SCOPES, subject='--my-email--')
service = build('admin', 'directory_v1', credentials=creds)
# Call the Admin SDK Directory API
#print('Getting the first 10 users in the domain')
request = service.users().list(customer='--customer-code--',
orderBy='email')
response = request.execute()
users = response.get('users', [])
while request:
request = service.users().list_next(previous_request=request, previous_response=response)
if request:
response = request.execute()
users.extend(response.get('users', []))
drive_service = build('drive', 'v3', credentials=creds)
results = drive_service.files().list(
pageSize=10, fields="").execute()
items = results.get('files', [])
if not users:
print('No users in the domain.')
else:
for user in users:
email = user['primaryEmail']
del_creds = creds.with_subject(email)
drive_service = build('drive', 'v3', credentials=del_creds)
# Call the Drive v3 API
results = drive_service.files().list(
pageSize=10, fields="").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']))
I figured it out. It turns out the first user in the list is suspended, and therefore does not have access to their Google drive files. Other users work just fine.
That begs the question, though, of how I'm supposed to look at the files of suspended users (as I'm pretty sure those files still exist).

Unable to generate refresh token for Google Ads API using generate_refresh_token.py

I am very new to Python and Stackoverflow. I am working on connecting my Google Ads account with Python to automate few standard charts creation and email them to my team members. Please help me resolve this as I had not been able to find an answer to it upon Googling either.
Let me know if I have missed out on any info which might provide more context to the question here.
I have been using the steps as mentioned by #msaniscalchi. Created client ID and client Secret from https://console.developers.google.com and created googleads.yaml file in the same directory as the generate_refresh_token.py. When I run the script with respective client ID and client Secret values, I am getting "invalid syntax" error. I have verified my multiple times my client secret and ID values multiple times.
"""Generates refresh token for AdWords using the Installed Application flow."""
import argparse
import sys
from google_auth_oauthlib.flow import InstalledAppFlow
from oauthlib.oauth2.rfc6749.errors import InvalidGrantError
# Your OAuth2 Client ID and Secret. If you do not have an ID and Secret yet,
# please go to https://console.developers.google.com and create a set.
DEFAULT_CLIENT_ID = 609XXXXXXX22-58mbhXXXXXXXXXXXXXXXXXX6ri.apps.googleusercontent.com
DEFAULT_CLIENT_SECRET = 7uO7XXXXXXXXXXXXXX7dKBAP
# The AdWords API OAuth2 scope.
SCOPE = u'https://www.googleapis.com/auth/adwords'
# The redirect URI set for the given Client ID. The redirect URI for Client ID
# generated for an installed application will always have this value.
_REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
parser = argparse.ArgumentParser(description='Generates a refresh token with '
'the provided credentials.')
parser.add_argument('--client_id', default=DEFAULT_CLIENT_ID,
help='Client Id retrieved from the Developer\'s Console.')
parser.add_argument('--client_secret', default=DEFAULT_CLIENT_SECRET,
help='Client Secret retrieved from the Developer\'s '
'Console.')
parser.add_argument('--additional_scopes', default=None,
help='Additional scopes to apply when generating the '
'refresh token. Each scope should be separated by a comma.')
class ClientConfigBuilder(object):
"""Helper class used to build a client config dict used in the OAuth 2.0 flow.
"""
_DEFAULT_AUTH_URI = 'https://accounts.google.com/o/oauth2/auth'
_DEFAULT_TOKEN_URI = 'https://accounts.google.com/o/oauth2/token'
CLIENT_TYPE_WEB = 'web'
CLIENT_TYPE_INSTALLED_APP = 'installed'
def __init__(self, client_type=None, client_id=None, client_secret=None,
auth_uri=_DEFAULT_AUTH_URI, token_uri=_DEFAULT_TOKEN_URI):
self.client_type = client_type
self.client_id = client_id
self.client_secret = client_secret
self.auth_uri = auth_uri
self.token_uri = token_uri
def Build(self):
"""Builds a client config dictionary used in the OAuth 2.0 flow."""
if all((self.client_type, self.client_id, self.client_secret,
self.auth_uri, self.token_uri)):
client_config = {
self.client_type: {
'client_id': self.client_id,
'client_secret': self.client_secret,
'auth_uri': self.auth_uri,
'token_uri': self.token_uri
}
}
else:
raise ValueError('Required field is missing.')
return client_config
def main(client_id, client_secret, scopes):
"""Retrieve and display the access and refresh token."""
client_config = ClientConfigBuilder(
client_type=ClientConfigBuilder.CLIENT_TYPE_WEB, client_id=client_id,
client_secret=client_secret)
flow = InstalledAppFlow.from_client_config(
client_config.Build(), scopes=scopes)
# Note that from_client_config will not produce a flow with the
# redirect_uris (if any) set in the client_config. This must be set
# separately.
flow.redirect_uri = _REDIRECT_URI
auth_url, _ = flow.authorization_url(prompt='consent')
print('Log into the Google Account you use to access your AdWords account '
'and go to the following URL: \n%s\n' % auth_url)
print('After approving the token enter the verification code (if specified).')
code = input('Code: ').strip()
try:
flow.fetch_token(code=code)
except InvalidGrantError as ex:
print('Authentication has failed: %s' % ex)
sys.exit(1)
print('Access token: %s' % flow.credentials.token)
print('Refresh token: %s' % flow.credentials.refresh_token)
if __name__ == '__main__':
args = parser.parse_args()
configured_scopes = [SCOPE]
if not (any([args.client_id, DEFAULT_CLIENT_ID]) and
any([args.client_secret, DEFAULT_CLIENT_SECRET])):
raise AttributeError('No client_id or client_secret specified.')
if args.additional_scopes:
configured_scopes.extend(args.additional_scopes.replace(' ', '').split(','))
main(args.client_id, args.client_secret, configured_scopes)
When I run the above code, I am getting the "Invalid Syntax" error highlighting at the numeric part of the Client ID and Secret.
Syntax error screenshot attached here
Editor Highlighter screenshot attached here

TypeError: file() argument 1 must be encoded string without NULL bytes, not str

I am trying to follow this tutorial to so connect to Google Analytics API. I followed everything step by step. But when I run the module in python, I get the following error:
Traceback (most recent call last):
File "C:\Users\me\Desktop\New folder (3)\HelloAnalytics.py", line 112, in <module>
main()
File "C:\Users\me\Desktop\New folder (3)\HelloAnalytics.py", line 106, in main
service_account_email)
File "C:\Users\me\Desktop\New folder (3)\HelloAnalytics.py", line 35, in get_service
service_account_email, key, scopes=scope)
File "C:\Python27\lib\site-packages\oauth2client\service_account.py", line 274, in from_p12_keyfile
with open(filename, 'rb') as file_obj:
TypeError: file() argument 1 must be encoded string without NULL bytes, not str
If anyone can point me in the right direction, that would be great. The full code is right here:
"""A simple example of how to access the Google Analytics API."""
import argparse
from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
import httplib2
from oauth2client import client
from oauth2client import file
from oauth2client import tools
def get_service(api_name, api_version, scope, key_file_location,
service_account_email):
"""Get a service that communicates to a Google API.
Args:
api_name: The name of the api to connect to.
api_version: The api version to connect to.
scope: A list auth scopes to authorize for the application.
key_file_location: The path to a valid service account p12 key file.
service_account_email: The service account email address.
Returns:
A service that is connected to the specified API.
"""
f = open(key_file_location, 'rb')
key = f.read()
f.close()
credentials = ServiceAccountCredentials.from_p12_keyfile(
service_account_email, key, scopes=scope)
http = credentials.authorize(httplib2.Http())
# Build the service object.
service = build(api_name, api_version, http=http)
return service
def get_first_profile_id(service):
# Use the Analytics service object to get the first profile id.
# Get a list of all Google Analytics accounts for this user
accounts = service.management().accounts().list().execute()
if accounts.get('items'):
# Get the first Google Analytics account.
account = accounts.get('items')[0].get('id')
# Get a list of all the properties for the first account.
properties = service.management().webproperties().list(
accountId=account).execute()
if properties.get('items'):
# Get the first property id.
property = properties.get('items')[0].get('id')
# Get a list of all views (profiles) for the first property.
profiles = service.management().profiles().list(
accountId=account,
webPropertyId=property).execute()
if profiles.get('items'):
# return the first view (profile) id.
return profiles.get('items')[0].get('id')
return None
def get_results(service, profile_id):
# Use the Analytics Service Object to query the Core Reporting API
# for the number of sessions within the past seven days.
return service.data().ga().get(
ids='ga:' + profile_id,
start_date='7daysAgo',
end_date='today',
metrics='ga:sessions').execute()
def print_results(results):
# Print data nicely for the user.
if results:
print 'View (Profile): %s' % results.get('profileInfo').get('profileName')
print 'Total Sessions: %s' % results.get('rows')[0][0]
else:
print 'No results found'
def main():
# Define the auth scopes to request.
scope = ['https://www.googleapis.com/auth/analytics.readonly']
# Use the developer console and replace the values with your
# service account email and relative location of your key file.
service_account_email = '<Replace with your service account email address.>'
key_file_location = '<Replace with /path/to/generated/client_secrets.p12>'
# Authenticate and construct service.
service = get_service('analytics', 'v3', scope, key_file_location,
service_account_email)
profile = get_first_profile_id(service)
print_results(get_results(service, profile))
if __name__ == '__main__':
main()
The error is being traced back to the ServiceAccountCredentials.from_p12_keyfile() function. It seems to be detecting a null value in the service_account_email string. you could make it a raw string by putting an 'r' before the first quote:
service_account_email = r'<Replace with your service account email address.>'
or by using a backslash '\' to escape the null value.
I ran into this problem yesterday. The problem is with the HelloAnalytics.py sample code. Replace the following three lines:
f = open(key_file_location, 'rb')
key = f.read()
f.close()
with this instead:
key = key_file_location
Unfortunately, the Google sample code tries to read the contents of the p12 file when it should just be pointing to the file location. The rest of the sample code ran fine for me without having to prefix my email or file location with r.
I had this same problem. I found it was the following line in the helloanalytics.py file. You need to modify line 33:
credentials = ServiceAccountCredentials.from_p12_keyfile(service_account_email, key, scopes=scope)
The ServiceAccountCredentials.from_p12_keyfile() function requires the key_file_location not the key.
Replace key with key_file_location:
credentials = ServiceAccountCredentials.from_p12_keyfile(service_account_email, key_file_location, scopes=scope)

Python - Create album with Picasa Error

Google have recently shifted to OAuth2.0 and we need to change our previous auth macanisms (i.e. from ProgrammaticLogin to OAuth2.0).
I can successfully access the albums and read the data / comments on photos. Its when i try to add new album/photo or try to write data i get the following error.
client = PhotosService(email="xxxx")
...
...
...
#After successfull OAuth
album = client.InsertAlbum(title="Temp album", summary="My summary", access="public")
This line causing the following error.
File "/Users/mac/destipak/env/lib/python2.7/site-packages/gdata/photos/service.py", line 358, in InsertAlbum
raise GooglePhotosException(e.args[0])
gdata.photos.service.GooglePhotosException: (403, 'Forbidden', 'Modification only allowed with api authentication.')
I wasn't quite sure but did you actually make the change over OAuth2? I used the following code and it worked.
def OAuth2Login(client_secrets, credential_store, email):
scope='https://picasaweb.google.com/data/'
user_agent='testingApp'
storage = Storage(credential_store)
credentials = storage.get()
if credentials is None or credentials.invalid:
flow = flow_from_clientsecrets(client_secrets, scope=scope, redirect_uri='urn:ietf:wg:oauth:2.0:oob')
uri = flow.step1_get_authorize_url()
webbrowser.open(uri)
code = raw_input('Enter the authentication code: ').strip()
credentials = flow.step2_exchange(code)
storage.put(credentials)
if (credentials.token_expiry - datetime.utcnow()) < timedelta(minutes=5):
http = httplib2.Http()
http = credentials.authorize(http)
credentials.refresh(http)
gd_client = gdata.photos.service.PhotosService(source=user_agent,
email=email,
additional_headers={'Authorization' : 'Bearer %s' % credentials.access_token})
return gd_client
album = gd_client.InsertAlbum('test', 'My Test Album', access='protected')
I did have to create an API Key in the Google developer portal and download the json secret but after doing that I was able to create an album successfully. This repo was very helpful https://github.com/MicOestergaard/picasawebuploader.

Google Admin SDK error Resource Not Found: domain when trying to list existing users

I am trying to write a simple script to get a list of my Google Apps users using Google's python API. So far it looks like this (based on a Google example):
!/usr/bin/python
import httplib2
from apiclient import errors
from apiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.client import SignedJwtAssertionCredentials
client_email = 'service_account_email#developer.gserviceaccount.com'
with open("Python GAPS-98dfb88b4c9f.p12") as f:
private_key = f.read()
OAUTH_SCOPE = 'https://www.googleapis.com/auth/admin.directory.user'
credentials = SignedJwtAssertionCredentials(client_email, private_key, OAUTH_SCOPE )
http = httplib2.Http()
http = credentials.authorize(http)
directory_service = build('admin', 'directory_v1', http=http)
all_users = []
page_token = None
params = {'customer': 'my_customer'}
while True:
try:
if page_token:
param['pageToken'] = page_token
current_page = directory_service.users().list(**params).execute()
all_users.extend(current_page['users'])
page_token = current_page.get('nextPageToken')
if not page_token:
break
except errors.HttpError as error:
print 'An error occurred: %s' % error
break
for user in all_users:
print user['primaryEmail']
The service account has been authorized on google developer console for the following API's:
https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/admin.directory.user.alias
However, when I run the code, I get this error:
An error occurred: <HttpError 404 when requesting https://www.googleapis.com/admin/directory/v1/users?customer=my_customer&alt=json returned "Resource Not Found: domain">
Any hints on what am I missing?
E.
Even when using a service account, you still need to "act as" a Google Apps user in the domain with the proper rights (e.g. a super admin). Try:
credentials = SignedJwtAssertionCredentials(client_email, private_key,
OAUTH_SCOPE, sub='admin#domain.com')
where admin#domain.com is the email of a super admin in your domain.

Categories