1

I have followed the steps mentioned in : https://developers.google.com/admin-sdk/directory/v1/guides/delegation

Service account has all the necessary domain wide delegations.

I wish to run below mentioned code in cloud function without passing credentials to build method, but it always returns 403- help appreciated

import pickle
import os.path
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/admin.directory.user']


def directory_api(request):
    """Shows basic usage of the Admin SDK Directory API.
    Prints the emails and names of the first 10 users in the domain.
    """
    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)
    print("before build")
    service = build('admin', 'directory_v1')

    # Call the Admin SDK Directory API
    print('Getting the first 10 users in the domain')
    try:
        results = service.users().list(domain="sss.com", viewType="domain_public").execute()
        print(results)
        users = results.get('users', [])
    except Exception as excs:
        print(excs)


    if not users:
        print('No users in the domain.')
    else:
        print('Users:')
        for user in users:
            print(u'{0} ({1})'.format(user['primaryEmail'],
                                      user['name']['fullName']))
    return "ok"

Sharath Kirani
  • 105
  • 1
  • 7
  • You talked about a Service Account, but the code you shared corresponds to authenticating with a regular account. Also, why do you think this can work without passing any credentials? – Iamblichus Jun 05 '20 at 10:53
  • Yes @lamblichus, you are right. But, I tried to deploy the above mentioned code in the CF and CF is deployed in the service account which has domain wide delegation. So code should not depend on the credential file. Should automatically detect the credentials. I have figured out the this using : https://stackoverflow.com/questions/60262432/service-account-not-authorized-to-access-this-resource-api-while-trying-to-acces/60262433#60262433 – Sharath Kirani Jun 08 '20 at 05:49
  • @SharathKirani Do you have a solution for the problem? I have the same issue and I'd love not to hard code the creds. – Seyeong Jeong Aug 23 '20 at 08:20
  • Looks like https://stackoverflow.com/a/60578000/1067717 is the answer to the question? – Seyeong Jeong Aug 23 '20 at 08:45

1 Answers1

4

Issue:

You are trying to access a private resource and not providing any credentials. Because of this, you are getting a 403.

Also, you talked about a Service Account, but the code you shared corresponds to authenticating with a regular account (you're not using these credentials anyway, but that's not how you build credentials with a Service Account).

Solution:

It's no use to grant domain-wide delegation (DWD) to a Service Account, if you don't do the following:

  • Use the Service Account credentials to impersonate a regular account who has access to the resource you are trying to access (probably an admin user in this case). The purpose of DWD is that the Service Account can act on behalf of any user in the domain. But you have to specify which user you want the Service Account to impersonate, otherwise the Service Account will behave as if you hadn't granted DWD at all.
  • Use the delegated credentials retrieved in previous step when building the service.

Actually, the page you referenced has an example of how to delegate credentials with a Service Account, check this.

An example more adapted to your needs, and using JSON instead of P12, could be this:

from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
SERVICE_ACCOUNT_FILE = 'credentials.json'

def listUsers():
    creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
    delegatedCreds = creds.with_subject('your-admin-account@sss.com')
    service = build('admin', 'directory_v1', credentials=delegatedCreds)
    # Call the Admin SDK Directory API
    print('Getting the first 10 users in the domain')
    try:
        results = service.users().list(domain="sss.com", viewType="domain_public").execute()
        print(results)
        users = results.get('users', [])
    except Exception as excs:
        print(excs)
    if not users:
        print('No users in the domain.')
    else:
        print('Users:')
        for user in users:
            print(u'{0} ({1})'.format(user['primaryEmail'],
                                      user['name']['fullName']))
    return "ok"

Reference:

Iamblichus
  • 18,540
  • 2
  • 11
  • 27
  • @lablichus. I appreciate your answer!Thanks! Code that you have mentioned above is perfect. Need to run the code in Cloud function and I cannot hard code credentials in the cloud function.So please let me know how do you pass the credentials with scope and subject to build('admin', 'directory_v1', credentials=delegatedCreds) – Sharath Kirani Jun 08 '20 at 05:51