How to create user in Google Admin with Service Account (server side) - python

I am trying to create a user in Google Admin via API
(google admin)
Things I have done:
create service account and credentials
installed sdk lib
read multiple docs on service account/Oauth credentials, google sdk authen., admin api
code
class Gcp:
def __init__(self):
SERVICE_ACCOUNT_FILE = "core/gcp/XXX.json"
read_domain_scope = 'https://www.googleapis.com/auth/admin.directory.domain.readonly'
admin_user_scope = 'https://www.googleapis.com/auth/admin.directory.user'
read_ou_scope = 'https://www.googleapis.com/auth/admin.directory.orgunit.readonly'
SCOPES = [read_domain_scope, admin_user_scope, read_ou_scope,]
self.credential = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
def list_user(self):
res = googleapiclient.discovery.build('users', 'v1', credentials=self.credential)
return 'OK'
Problems:
I can not find a document which specify how I can create user in google admin via SDK, I mean I've found the REST api doc
But I seems that I need to authenticate through OAuth2 for the REST calls.
Is there a way to get this (create user and assign domain/organization unit) done with just service account credential file with SDK?

Related

Python script for google drive is redirecting to chrome authentication

I write a python script to upload file to google drive, but the script is redirecting to chrome for email user authentication.
is there any way to avoid redirecting to chrome for authentication.
I'm running on python 3.9.
here is my sample code:
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
gauth = GoogleAuth()
drive = GoogleDrive(gauth)
upload_file_list = ['myfile.pdf']
for upload_file in upload_file_list:
gfile = drive.CreateFile({'parents': [{'id': '1B8ttlQMRUkjbrscevfa1DablIayzObh2'}]})
# Read file and set it as the content of this instance.
gfile.SetContentFile(upload_file)
gfile.Upload() # Upload the file.
The behaviour you are reporting is totally normal with OAuth 2.0 and the official Google APIs library.
What #Tanaike said is a good solution. You could use a service account to access Google Drive files without granting consent every time the token expires. With service accounts there are 2 options to achieve that:
Share the file/folder with the email address of the service account.
Use domain-wide delegation of authority to allow the service account to impersonate any user in your domain. Requires a domain using Google Workspace or Cloud Identity and Super Admin access to configure domain-wide delegation.
General information on how to make API calls with domain-wide delegation is available on this page https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests.
Here is a working code sample:
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# Scopes required by this endpoint
# https://developers.google.com/drive/api/v3/reference/permissions/list
SCOPES = ["https://www.googleapis.com/auth/drive.readonly"]
# Variable that holds the file ID
DOCUMENT_ID = "i0321LSy8mmkx_Bw-XlDyzQ_b3Ny9m74u"
# Service account Credential file downloaded with domain-wide delegation of authority
# or with shared access to the file.
SERVICE_ACCOUNT_FILE = "serviceaccount.json";
# Creation of the credentials
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE,
scopes=SCOPES)
# [Impersonation] the service account will take action on behalf of the user,
# requires domain-wide delegation of authority.
delegated_credentials = credentials.with_subject('user#domain.com')
# The API call is attempted
try:
service = build('drive', 'v3', credentials=delegated_credentials)
# Retrieve the documents contents from the Docs service.
document = service.files().get(fileId=DOCUMENT_ID).execute()
print('The title of the document is: {}'.format(document.get('name')))
except HttpError as err:
print(err)
Keep in mind that to use user impersonation you will need to configure domain-wide delegation in the Admin console of the domain that has the files (this will also work for external files shared with users in the domain).
If you want to use this with regular consumer accounts you can't use user impersonation, instead you will share the file with the service account (read or write access) to later make API calls. Line 20 creates delegated credentials, this line needs to be removed if you will use this other approach.

Google Directory API Python ETL

I'm trying to build a directory sync ETL for Google Workspace, but I'm getting 403's from the code snippet.
from google.oauth2 import service_account
from googleapiclient.discovery import build
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user.readonly']
SERVICE_ACCOUNT_FILE = './credentials.json' #TODO: these creds need to be passed in more safely.
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = build('admin', 'directory_v1', credentials=credentials)
results = service.users().list(domain='mydomain.com').execute()
users = results.get('users', [])
The service account has been given domain-wide delegation to the listed scope and should be able to access the API. Other similar posts have mentioned that a domain administrator must approve the request, but that doesn't make sense in the case where I need this to run multiple times a week without any administrator intervention.
Using the Users API requires the User Management Admin role (or an equivalent custom role). You can grant this role to a service account, then you won't need domain-wide delegation at all.

Connect to microsoft graph API

I have created an azure application using the Microsoft azure platform.
using the below script I make an attempt to connect to the API using the credentials given when creating the azure application.
from O365 import Account
credentials = ('azureApp_clientId', 'azureApp_clientSecret')
account = Account(credentials)
if account.authenticate(scopes=['Mail.Read']):
print('Authenticated!')
When the script runs it returns a URL to add to a browser and give consent..
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?xxxxxxxxxx
When i paste the URL into my browser it does nothing and returns a blank page..
This is my redirect URI in the azure app
What am I missing??
Docs to o365 Lib here https://pypi.org/project/O365/
Update
from O365 import Account
credentials = ('myclientID')
account = Account(credentials, auth_flow_type = 'public')
if account.authenticate(scopes = ['Mail.Read']):
print('Authenticated!')
mailbox = account.mailbox()
inbox = mailbox.inbox_folder()
for message in inbox.get_messages():
print(message)
Update
According to the configuration of the application azure (you register the application as Mobile and desktop applications), you should use the method Authenticate on behalf of a user (public) to do auth and should not provide client_secret. For more details, please refer to here and here.
For example
from O365 import Account
credentials = ('<your client_id>',)
account = Account(credentials,auth_flow_type='public')
if account.authenticate(scopes==['Mail.Read'] ):
print('Authenticated!')
mailbox = account.mailbox()
inbox = mailbox.inbox_folder()
for message in inbox.get_messages():
print(message)
#Update
My configuration

Using Google People API with Service Account

I'm using the Google People API to access my contacts.
I activated it in the Google Developers Console and created a project, a service account (ending with ....iam.gserviceaccount.com) and a key for authentication which is stored in JSON format.
When I access the contacts, it seems to take the contacts of my service account address rather than my Google account which results in an empty list.
How can I tell the API to use my account rather than the service account?
This is the code I have so far:
from google.oauth2 import service_account
from googleapiclient.discovery import build
# pip install google-auth google-auth-httplib2 google-api-python-client
SCOPES = ['https://www.googleapis.com/auth/contacts.readonly']
KEY = '~/private.json'
credentials = service_account.Credentials.from_service_account_file(
KEY, scopes=SCOPES)
service = build(
serviceName='people', version='v1', credentials=credentials)
connections = service.people().connections().list(
resourceName='people/me', personFields='names').execute()
print(connections)
# result: {}
A service account is NOT you a service account is a dummy user it has its own google drive account, google calendar and apparently google contacts. The reason that you are seeing an empty result set is that you have not added any contacts to the service accounts account.
Service accounts are most often used to grant access to data that the developer owns. For example you can take the service account email address and share one of your folders on google drive it will then have acccess to that folder on your google drive account. You can do the same with google calendar.
There are some apis that do not give you the ablity to share your data with other users. Youtube, adwords, blogger and google contacts to name a few.
You cant use a service account to access your personal google contacts. Your best bet would be to authenticate your application with oauth2 and access them that way.
Note about Google Workspace
If you have a google workspace account, a serivce account can be configured to act on behalf of a user on the domain, but only a user on the domain. Perform Google Workspace domain-wide delegation of authority
Not a python expert but I've just performed the task the OP is talking about in .NET and I am pretty sure it's feasable with Python too.
So it looks like all needs to be done is delegating domain-wide authority to the SA. I.e. assign required scopes for your SA, in my case it was https://www.googleapis.com/auth/contacts.readonly.
Then you should do your call and specify an account you're trying to impersonate (took the python example from here)
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
# this is the line you apparently were missing
delegated_credentials = credentials.with_subject('user#example.org')
Then you'll be able to do the people/me calls. Worked for me in .NET as I said.

Google App Engine (Python), OAuth2, Service Accounts, User Delegation, Youtube Service

I'm using the Youtube Data API (v3), along with the Python client library on Google App Engine.
Here is a simple snippet of code that fails:
kwargs = {'prn' : google_oauth_user_email}
credentials = SignedJwtAssertionCredentials(google_oauth_email, google_oauth_key, scope=YOUTUBE_SCOPE, **kwargs)
http = credentials.authorize(httplib2.Http())
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, http=http)
request = youtube.channels().list(part="snippet", id="25")
response = request.execute(http=http)
which results in:
Failed to retrieve access token: {
"error" : "access_denied"
}
If I remove the kwargs so as not to delegate user access, this request works fine. The Service Account is registered against the same user account to which delegation is being requested.
My ultimate goal is to upload videos (on behalf of that user) using the Server-to-Server two-legged OAuth2 flow.
Can anybody explain to me why delegation to the same user account as that which the Service Account is already registered fails, and how I might be able to allow it?

Categories