Google - Directory API (Admin SDK) Create Group - Error - python

I am currently following Google's Python Quickstart for Directory API and experimenting with different ways of using the REST API's. The tutorial is designed to run within the Command Line and authenticates with O Auth 2.0 by creating a I have managed to change the code of the tutorial so that I can insert a user into a group that is already existing, by passing a groupKey of the group I am wishing to insert a User into and the body containing the email of the User I am adding, however, I cannot get Create a Group to work.
The error that I am getting is:
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/admin/directory/v1/users?alt=json returned "Insufficient Permission">
I am almost positive that my permissions within the Google Cloud Project are correct as I am a editor, and within G Suite I am a Super Admin. At first I thought I might of been pointing to the wrong Scope, but it appears that that is the recomended scope for creating the Group.
Could somebody please aid me in getting a group created, as I am very new to REST API's and wish to improve my own skills and understanding. Whether there has been an area that I simply misunderstood and need to go back and read on.
I have posted my code below:
from __future__ import print_function
import httplib2
import os
from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
SCOPES = 'https://www.googleapis.com/auth/admin.directory.group'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'generic-app-name' # removed for example - imagine the name is included
def get_credentials():
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'admin-directory_v1-python-quickstart.json')
store = Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatibility with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
def main():
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('admin', 'directory_v1', http=http)
print('Creating Group For Testing')
test = {
"email" : "test#test.co.uk", # Fake email for example
"name" : "Test Group",
"description" : "Just testing here."
}
group = service.users().insert(body = test).execute()
return group
if __name__ == '__main__':
main()
Thank you for taking the time to read the post and I hope to hear back from some of you soon,
A hopeful programmer.

You can follow the link The Google Directory Groups API for python for create a group in Google Directory.

I found the solution after looking at the Error Logs within the Google Cloud project, rather a simple fix.
Before:
group = service.users().insert(body = test).execute()
After:
group = service.groups().insert(body = test).execute()
Here you can see that I have changed service.users to service.groups.
I cannot believe I let such a simple mistake get past me, but leaving on here incase this helps others.
Thank you

Related

How to use credentials obtained from google with google API

Libs: dj-rest-auth + allauth
I. I'm trying to interact with google API with credentials that I use to obtain internal access token. I managed to obtain both code and token but can't find how to use them with google API. The last page I found inapplicable is https://github.com/googleapis/google-api-python-client/blob/main/docs/oauth.md but probably I'm missing some things.
Here's the view I'm trying to use google API in:
class CreateGoogleDoc(GenericAPIView):
...
def get(self, request):
token = request.query_params['token']
module_dir = os.path.dirname(__file__) # get current directory
file_path = os.path.join(module_dir, 'client_secret.json')
flow = Flow.from_client_secrets_file(
file_path,
scopes=SCOPES,
redirect_uri='https://example.com'
)
credentials = service_account.Credentials.from_service_account_file(file_path, scopes=SCOPES)
service = build('docs', 'v1', credentials=credentials)
document = service.documents().create().execute()
return Response([document['documentId']])
II. While I tried to swap code to internal access token class I got another error:
Error retrieving access token: `{ "error": "invalid_request", "error_description": "You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy for keeping apps secure. You can let the app developer know that this app doesn't comply with one or more Google validation rules."}`
Here's a view that I'm using for swapping:
GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
callback_url = 'http://localhist:8000/dj-rest-auth/google/'
client_class = OAuth2Client
Thanks!
Offering a workaround
If you already have a token from the GET response, why are you trying to get credentials from a service account file? Probably there is some wrong configuration there, but if you already have the access token, you can just use it like below and avoid the whole service account token fetching.
from google.oauth2.credentials import Credentials
# ...
def get(self, request):
token = request.query_params['token']
credentials = Credentials(token)
service = build('docs', 'v1', credentials=credentials)
document = service.documents().create().execute()
return Response([document['documentId']])

googleapi.discovery iam create service account

Hi I want to use the google api service to create service accounts.
Here is my current code:
base_url = f"https://iam.googleapis.com/v1/projects/{project}/serviceAccounts"
auth = f"?access_token={access_token}"
data = {"accountId": name,
"serviceAccount": {
"displayName": name
}}
Create a service Account
r = requests.post(base_url + auth, json=data)
try:
r.raise_for_status()
except requests.HTTPError:
if r.status_code != 409:
raise
This works, but it uses the requests package.
I want to use googleapiclient
from googleapiclient.discovery import build
credentials = GoogleCredentials.get_application_default()
api = build(service, version, credentials=credentials)
Then, where do I find information on how to use this api object?
I've tried:
api.projects().serviceAccounts.create(name=name).execute()
But this does not work, and I don't know how to find what arguments are expected or required.
You can find the GCP IAM API documentation here.
The arguments required and values are documented there.
For anyone else who is struggling.
Check out api explorer to get the format of the request.
For example, If the endpoint is iam.projects.serviceAccounts.get
and you need to provide name = "projects/project/serviceAccounts/sa#gsc.googleserviceaccounts.com"
Then your call will look like:
from googleapiclient.discovery import build
credentials = GoogleCredentials.get_application_default()
api = build(service, version, credentials=credentials)
sa = api.projects().serviceAccounts().get(name="projects/project/serviceAccounts/sa#gsc.googleserviceaccounts.com")
Hope this helps someone.

People API returns no connections when authenticating via Service Account [duplicate]

I'm trying to programmatically access the list of contacts on my own personal Google Account using the Python Client Library
This is a script that will run on a server without user input, so I have it set up to use credentials from a Service Account I set up. My Google API console setup looks like this.
I'm using the following basic script, pulled from the examples provided in the API docs -
import json
from httplib2 import Http
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
# Only need read-only access
scopes = ['https://www.googleapis.com/auth/contacts.readonly']
# JSON file downloaded from Google API Console when creating the service account
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'keep-in-touch-5d3ebc885d4c.json', scopes)
# Build the API Service
service = build('people', 'v1', credentials=credentials)
# Query for the results
results = service.people().connections().list(resourceName='people/me').execute()
# The result set is a dictionary and should contain the key 'connections'
connections = results.get('connections', [])
print connections #=> [] - empty!
When I hit the API it returns a result set without any 'connections' key. Specifically it returns -
>>> results
{u'nextSyncToken': u'CNP66PXjKhIBMRj-EioECAAQAQ'}
Is there something pertaining to my setup or code that's incorrect? Is there a way to see the response HTTP status code or get any further detail about what it's trying to do?
Thanks!
Side note: When I try it using the "Try it!" feature in the API docs, it correctly returns my contacts. Although I doubt that uses the client library and instead relies on user authorization via OAuth
The personFields mask is required. Specify one or more valid paths. Valid paths are documented at https://developers.google.com/people/api/rest/v1/people.connections/list/.
Additionally, use fields mask to specify which fields are included in a partial response.
Instead of:
results = service.people().connections().list(resourceName='people/me').execute()
... try:
results = service.people().connections().list(resourceName='people/me',personFields='names,emailAddresses',fields='connections,totalItems,nextSyncToken').execute()
Here is a working demo. I just tested it right now. Python 3.5.2
google-api-python-client==1.6.4
httplib2==0.10.3
oauth2client==4.1.2
You can save it to demo.py and then just run it. I left the create_contact function in case you might want to use it and have one more example on the API usage.
CLIENT_ID and CLIENT_SECRET are environment variables so I don't accidentally share that in code.
"""Google API stuff."""
import httplib2
import json
import os
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run_flow
CLIENT_ID = os.environ['CLIENT_ID']
CLIENT_SECRET = os.environ['CLIENT_SECRET']
SCOPE = 'https://www.googleapis.com/auth/contacts'
USER_AGENT = 'JugDemoStackOverflow/v0.1'
def make_flow():
"""Make flow."""
flow = OAuth2WebServerFlow(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=SCOPE,
user_agent=USER_AGENT,
)
return flow
def get_people():
"""Return a people_service."""
flow = make_flow()
storage = Storage('info.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run_flow(flow, storage)
http = httplib2.Http()
http = credentials.authorize(http)
people_service = build(serviceName='people', version='v1', http=http)
return people_service
def create_contact(people, user):
"""Create a Google Contact."""
request = people.createContact(
body={
'names': [{'givenName': user.name}],
'phoneNumbers': [
{'canonicalForm': user.phone, 'value': user.phone}],
}
)
return request.execute()
def demo():
"""Demonstrate getting contacts from Google People."""
people_service = get_people()
people = people_service.people()
connections = people.connections().list(
resourceName='people/me',
personFields='names,emailAddresses,phoneNumbers',
pageSize=2000,
)
result = connections.execute()
s = json.dumps(result)
# with open('contacts.json', 'w') as f:
# f.write(s)
return s
if __name__ == '__main__':
print(demo())
With service account, in DwD - G Suite Domain-wide Delegation, is necessary impersonate or delegate user in this way
delegate = credentials.create_delegated('user#xxxx.xxx')
For fellow googlers: I have the same problem using the JS API.
I succeded on my personal gmail address, but not on my work one (g-suite) neither on my secondary gmail address.
Can't see the pattern. It's possible that the work one has contact listing deactivated.

Getting WebViewLinks with Google Drive

I've just started trying to use the Google Drive API. Using the quickstart guide I set up the authentication, I can print a list of my files and I can even make copies. All that works great, however I'm having trouble trying to access data from a file on Drive. In particular, I'm trying to get a WebViewLink, however when I call .get I receive only a small dictionary that has barely any of the file's metadata. The documentation makes it look like all the data should just be there by default but it's not appearing. I couldn't find any way to flag for requesting any additional information.
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('drive', 'v3', http=http)
results = service.files().list(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(item['name'], item['id'])
if "Target File" in item['name']:
d = service.files().get(fileId=item['id']).execute()
print(repr(d))
This is the output of the above code: (the formatting is my doing)
{u'mimeType': u'application/vnd.google-apps.document',
u'kind': u'drive#file',
u'id': u'1VO9cC8mGM67onVYx3_2f-SYzLJPR4_LteQzILdWJgDE',
u'name': u'Fix TVP Licence Issues'}
For anyone confused about the code there is some missing that's just the basic get_credentials function from the API's quickstart page and some constants and imports. For completeness, here's all that stuff, unmodified in my code:
from __future__ import print_function
import httplib2
import os
from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools
SCOPES = 'https://www.googleapis.com/auth/drive'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Drive API Python Quickstart'
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'drive-python-quickstart.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatibility with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
So what's missing, how can I get the API to return all that extra meta data that's just not appearing right now?
You are very close. With the newer version of the Drive API v3, to retrieve other metadata properties, you will have to add the fields parameter to specify additional properties to include in a partial response.
In your case, since you are looking to retrieve the WebViewLinkproperty your request should look something similar to this:
results = service.files().list(
pageSize=10,fields="nextPageToken, files(id, name, webViewLink)").execute()
To display your items from the response:
for item in items:
print('{0} {1} {2}'.format(item['name'], item['id'], item['webViewLink']))
I also suggest try it out with the API Explorer so you can view what additional metadata properties you would like to display on your response.
Good Luck and Hope this helps ! :)
You explicitly request only the id and name fields in your files.list call. Add webViewLink to the list to results = service.files().list(fields="nextPageToken, files(id, name, webViewLink)").execute(). To retrieval all metadata files/* should be used. For more information about this performance optimizations see Working with partial resources in the Google Drive docs.
I have written a custom function to help with getting a sharable web link given a file/folder id. More information can be gotten here
def get_webViewLink_by_id(spreadsheet_id):
sharable_link_response = drive_service.files().get( fileId=spreadsheet_id, fields='webViewLink').execute()
return(sharable_link_response['webViewLink'])
print(get_webViewLink_by_id(spreadsheet_id = '10Ik3qXK4wseva20lNGUKUTBzKoywaugi6XOmRUoP-4A'))

Looking for example using MediaFileUpload

Does anyone know where I can find complete sample code for uploading a local file and getting contents with MediaFileUpload?
I really need to see both the HTML form used to post and the code to accept it. I'm pulling my hair out and so far only getting partial answers.
I found this question while trying to figure out where the heck "MediaFileUpload" came from in the Google API examples, and I eventually figured it out. Here is a more complete code example that I used to test things with Python 2.7.
You need a JSON credentials file for this code to work. This is the credentials file you get from your Google app / project / thing.
You also need a file to upload, I'm using "test.html" here in the example.
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
from apiclient.http import MediaFileUpload
#Set up a credentials object I think
creds = ServiceAccountCredentials.from_json_keyfile_name('credentials_from_google_app.json', ['https://www.googleapis.com/auth/drive'])
#Now build our api object, thing
drive_api = build('drive', 'v3', credentials=creds)
file_name = "test"
print "Uploading file " + file_name + "..."
#We have to make a request hash to tell the google API what we're giving it
body = {'name': file_name, 'mimeType': 'application/vnd.google-apps.document'}
#Now create the media file upload object and tell it what file to upload,
#in this case 'test.html'
media = MediaFileUpload('test.html', mimetype = 'text/html')
#Now we're doing the actual post, creating a new file of the uploaded type
fiahl = drive_api.files().create(body=body, media_body=media).execute()
#Because verbosity is nice
print "Created file '%s' id '%s'." % (fiahl.get('name'), fiahl.get('id'))
A list of valid Mime Types to use in the "body" hash is available at
https://developers.google.com/drive/v3/web/mime-types
A list of valid mimetype strings for the MediaFileUpload (they'll attempt to convert your file to whatever you put here):
https://developers.google.com/drive/v3/web/integrate-open#open_files_using_the_open_with_contextual_menu
Python 2.7, resumable upload.
https://github.com/googleapis/google-api-python-client/blob/master/docs/media.md
from __future__ import print_function
import pickle
import os.path
from googleapiclient.http import MediaFileUpload
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/drive']
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.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.json', 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('drive', 'v3', credentials=creds)
media = MediaFileUpload(
'big.jpeg',
mimetype='image/jpeg',
resumable=True
)
request = service.files().create(
media_body=media,
body={'name': 'Big', 'parents': ['<your folder Id>']}
)
response = None
while response is None:
status, response = request.next_chunk()
if status:
print("Uploaded %d%%." % int(status.progress() * 100))
print("Upload Complete!")
if __name__ == '__main__':
main()
You won't need to post JSON yourself, the client library handles that for you.
We provide full code samples already which can be found here: https://github.com/gsuitedevs/python-samples
Also you could check the file.insert reference documentation which contains a Python sample: https://developers.google.com/drive/v2/reference/files/insert
If this does not answer what you want perhaps you could explain in more details what you want to achieve and your architecture currently in place.
I want to provide additional information on uploading to a specific drive folder. I am providing the example from my AWS Lambda with Python 3.7
Note:
You need the folder ID for your desired location. You can find this by going to your drive folder and looking for the ID in the URL in the Browser.
For example in this URL, https://drive.google.com/drive/u/0/folders/1G91IKgQqI9YgNj8Odc8SIOPHrWOjdvOO, 1G91IKgQqI9YgNj8Odc8SIOPHrWOjdvOO would be your ID.
You need to provide your service account email access to the folder to be accessed. The service account email is found in the IAM section of your Google Cloud account. Add access to your folder by going to it in Drive, clicking the "i" icon on the top right, clicking details, then manage access.
You also need the JSON file associated with the service account. Find/create this in the Service Accounts section in Google Cloud IAM on the KEYS tab. The file contains the private key for your project. Store it where your code can access.
I'm not sure which dependencies you need to install but I think
pydrive installed them all for me: pip3 install pydrive
from apiclient.discovery import build
from google.oauth2 import service_account
from googleapiclient.http import MediaFileUpload
# This provides what authority the service account has as well as the location of the JSON file containing the private key.
scopes = ['https://www.googleapis.com/auth/drive']
service_account_file = 'path/to/service_account.json'
# Create the credentials object for the service account
credentials = service_account.Credentials.from_service_account_file(service_account_file, scopes=scopes)
drive = build('drive', 'v3', credentials=credentials)
# Create the metadata for the file and upload it to the drive folder. Supply the corresponding MIME type for your file. The parents parameter is very important, this is where you supply the ID you found for your drive folder.
body = {'name': 'testfile.txt', 'mimeType': 'text/plain', 'parents': ["theStringForTheDriveFolder"]}
media = MediaFileUpload('path/to/testfile.txt', mimeType='text/plain')
drive.files().create(body=body, media_body=media).execute()
Here's the documentation I followed:
How to use service accounts to call google APIs: https://developers.google.com/identity/protocols/oauth2/service-account#python
Documentation for the Drive API V3 to upload files:
https://developers.google.com/drive/api/v3/reference/files/create
Documentation for the Google-Python API client:
https://github.com/googleapis/google-api-python-client/blob/main/docs/oauth.md
How to perform various upload types with the Drive API:
https://developers.google.com/drive/api/guides/manage-uploads#simple
How to create and use Service Accounts, including generating the JSON
file/private keys:
https://developers.google.com/identity/protocols/oauth2/service-account

Categories