Open simonw opened 2 years ago
Creating a service account:
gcloud iam service-accounts create sfms-history-google-drive \
--display-name="sfms-history-google-drive" \
--description="Access to the Google Drive for sfms-history" \
--project sfms-history
It then shows up in:
gcloud iam service-accounts list --project sfms-history
Then create a key like this:
gcloud iam service-accounts keys create /tmp/sfms-history-google-drive.key.json \
--iam-account="sfms-history-google-drive@sfms-history.iam.gserviceaccount.com" \
--project sfms-history
Importantly, you can share a Google Drive folder with the email address sfms-history-google-drive@sfms-history.iam.gserviceaccount.com
and that service account will now have access to the folder!
I managed to that access programatically using the Google Client libraries:
pip install google-api-python-client
Then:
from google.oauth2 import service_account
from googleapiclient import discovery
scopes = ["https://www.googleapis.com/auth/drive.readonly"]
service_account_file = "/tmp/sfms-history-google-drive.key.json"
credentials = service_account.Credentials.from_service_account_file(service_account_file, scopes=scopes)
drive = discovery.build('drive', 'v3', credentials=credentials)
drive.files().list().execute()
Output starts:
{'kind': 'drive#fileList',
'nextPageToken': '~!!~AI9FV7St9uKeYX_Iqjg-GvyLDLH37H64zM7NHxq6lBpA_M3WryVvEBzCDBkWxUPRFay8VRv5zvOxSP-OPqLEGQEnOVtwoMtkrtVfO6Y2_irvGVaZR3YQ6anTrdphTLl2HIuATsd07ugWsnZ3pnWGPE45C-mYmrF5qJ5VBHgrmtIXu3gIB2f-mSh_AqAeTRLV1xA8AhMHj6QA9wvx_Fq9PX7w01nZvw5gPAYfOnl_CcdKqg4mcZijZjwJf5fn3kn1lIGcHSF91YORt8PKJQw1RD4mUCcKD9nQZNOPTi67M20lqaEt54D5Akg=',
'incompleteSearch': False,
'files': [{'kind': 'drive#file',
'id': '1EcfXj1Ah3OYX3XqETPGJ1rP7ak53srKf',
'name': 'SFMS History',
'mimeType': 'application/vnd.google-apps.folder'},
{'kind': 'drive#file',
'id': '1E0NDxHx6yBAwpB7Ay8X3-QydETZQKHwmj5e3PvU72mo',
'name': 'SFMS Records',
'mimeType': 'application/vnd.google-apps.spreadsheet'},
{'kind': 'drive#file',
'id': '14mqmth3mEDpJ0ww6vQF_Vg8VsJm-U_hB',
'name': 'Studies in Microscopical Science, Edited by Arthur C Cole FRMS, Volume 1, parts 1 to 52, 1882-1883 (CD from Henry Schott)',
I want to make direct HTTP authenticated calls to the API though, since that's what the code I have written already does.
This works:
import google.auth.transport.requests
from google.auth import _helpers
import httpx
credentials = service_account.Credentials.from_service_account_file(service_account_file, scopes=scopes)
# Weird trick needed to populate credentials.token:
credentials.refresh(google.auth.transport.requests.Request())
response = httpx.get("https://www.googleapis.com/drive/v3/files", headers={
"authorization": "Bearer {}".format(_helpers.from_bytes(credentials.token))
})
response.json()
Source code of that from_bytes
helper function:
def from_bytes(value):
"""Converts bytes to a string value, if necessary.
Args:
value (Union[str, bytes]): The value to be converted.
Returns:
str: The original value converted to unicode (if bytes) or as passed in
if it started out as unicode.
Raises:
ValueError: If the value could not be converted to unicode.
"""
result = value.decode("utf-8") if isinstance(value, six.binary_type) else value
if isinstance(result, six.text_type):
return result
else:
raise ValueError("{0!r} could not be converted to unicode".format(value))
Service account keys take the form of a JSON file on disk containing a primary key.
It's possible, albeit non-obvious, to make calls to the Google Drive API using these keys.
They might represent a better authentication mechanism for many use-cases. See also:
39