Skip to content

Commit 15789e0

Browse files
committed
[chatgpt] Add Google Drive access policy Admin API example
1 parent a78f3f3 commit 15789e0

4 files changed

Lines changed: 1192 additions & 0 deletions

File tree

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# Manage Google Drive access with the ChatGPT Admin API
2+
3+
ChatGPT workspace administrators can choose which Google shared drives the
4+
Google Drive app may access and configure My Drive access separately. This
5+
example previews and updates those settings through the ChatGPT Admin API.
6+
7+
The [administration script](google_drive_access_admin.py) uses only the Python
8+
standard library. It accepts shared-drive IDs or root URLs, imports CSV/text
9+
files, and supports replacing, adding to, or removing from a selected-drive
10+
allowlist.
11+
12+
> **Availability:** This example targets the Google Drive access-policy Admin
13+
> API. Confirm that the endpoint and Google Drive policy enforcement are
14+
> enabled for your workspace before relying on a policy to restrict access.
15+
> A successful policy read or save alone does not establish that enforcement
16+
> is active.
17+
18+
## Understand the policy
19+
20+
All operations use this URL, where `<workspace-id>` is your ChatGPT workspace UUID:
21+
22+
```text
23+
https://api.chatgpt.com/v1/manage/workspaces/<workspace-id>/google-drive/drive-access/allow-list
24+
```
25+
26+
| Method | Behavior |
27+
| --- | --- |
28+
| `GET` | Read the shared-drive allowlist and My Drive setting. |
29+
| `PUT` | Replace the shared-drive allowlist; optionally update My Drive. |
30+
| `DELETE` | Allow all shared drives; preserve the current My Drive setting. |
31+
32+
Every `PUT` requires `drive_ids`. Its three possible states have different meanings:
33+
34+
| `drive_ids` | Shared-drive access |
35+
| --- | --- |
36+
| `null` | All shared drives permitted by the user's Google permissions. |
37+
| `[]` | No shared drives. |
38+
| `["0AExampleFinanceDrive"]` | Only the listed shared drives, subject to Google permissions. |
39+
40+
`allow_personal_drive` is a separate boolean. Omit it, or send `null`, to preserve
41+
the setting observed by the server. Its default is `true`. Setting it to `false`
42+
blocks files outside shared drives, including files shared from another user's
43+
My Drive. To block both categories, use `drive_ids: []` and
44+
`allow_personal_drive: false` together.
45+
46+
The API supports whole shared drives, with at most 1,000 IDs per request. IDs
47+
are case sensitive and contain 5–512 ASCII letters, digits, underscores, or
48+
hyphens. The script deduplicates IDs before sending them. The API does not
49+
resolve names or URLs, verify that IDs exist, grant Google permissions, or
50+
support drive exclusion lists or individual file/folder policies.
51+
52+
## Set up credentials
53+
54+
You need Python 3.10 or later, your workspace UUID, and a ChatGPT workspace
55+
Admin API key with administrator permissions in the workspace's organization.
56+
All three methods, including `GET`, require `chatgpt.enterprise.apps.write`.
57+
58+
In the ChatGPT Admin Console, select your workspace, open **Credentials**, and
59+
choose the **Admin keys** tab. Create a key with **Restricted** permissions and
60+
set **Apps** to **Write**. An OpenAI API Platform key cannot replace this
61+
workspace Admin API key.
62+
63+
In Bash, prompt for the key without putting its value in shell history:
64+
65+
```bash
66+
read -r -s -p "ChatGPT workspace admin key: " CHATGPT_ADMIN_TOKEN
67+
echo
68+
export CHATGPT_ADMIN_TOKEN
69+
70+
export WORKSPACE_ID="<workspace-id>"
71+
```
72+
73+
Replace `<workspace-id>` with your workspace UUID. Run the administration
74+
commands below from `examples/chatgpt/google_drive_access` in a checkout of this
75+
repository. No third-party Python packages are required.
76+
77+
### Optional Google Drive credential
78+
79+
The `inspect` command and any command using root URLs also require
80+
`GOOGLE_DRIVE_TOKEN`. Obtain a Google OAuth access token with the
81+
`https://www.googleapis.com/auth/drive.readonly` scope, using your organization's
82+
approved OAuth application with the Google Drive API enabled. The token's user
83+
must be able to read the shared drives' metadata. See Google's
84+
[OAuth setup guide](https://developers.google.com/workspace/drive/api/quickstart/python#authorize_credentials_for_a_desktop_application)
85+
for an example credential setup.
86+
87+
If you adapt that quickstart, request `drive.readonly` instead of its sample
88+
`drive.metadata.readonly` scope and authorize again after changing scopes.
89+
Supply the resulting access token, rather than a client secret or refresh token.
90+
The administration script does not obtain or refresh Google tokens.
91+
92+
```bash
93+
read -r -s -p "Google Drive access token: " GOOGLE_DRIVE_TOKEN
94+
echo
95+
export GOOGLE_DRIVE_TOKEN
96+
```
97+
98+
The script uses Google's
99+
[`drives.get` method](https://developers.google.com/workspace/drive/api/reference/rest/v3/drives/get)
100+
to verify a shared-drive ID and retrieve its name. It does not request domain
101+
administrator access. Names appear only in inspection and preview output; the
102+
ChatGPT policy receives IDs. ID-only policy commands do not require a Google
103+
token and perform syntax validation only.
104+
105+
## Inspect shared drives
106+
107+
Select a shared drive in Google Drive and copy the URL of its root. Replace the
108+
illustrative ID below with your drive's actual ID:
109+
110+
```bash
111+
python3 google_drive_access_admin.py inspect \
112+
--drive-url https://drive.google.com/drive/folders/0AExampleFinanceDrive
113+
```
114+
115+
URLs containing an account index, such as `/drive/u/0/folders/ID`, are also
116+
accepted. A folder URL can look like a drive-root URL, so the script validates
117+
the extracted ID with `drives.get`. It rejects an ordinary folder instead of
118+
expanding the request to its containing shared drive. You can also inspect an
119+
ID directly with `--drive-id`. Inspection calls only Google and does not require
120+
the ChatGPT Admin API key.
121+
122+
## Read the current policy
123+
124+
```bash
125+
python3 google_drive_access_admin.py list --workspace-id "$WORKSPACE_ID"
126+
```
127+
128+
A policy allowing selected shared drives while blocking My Drive looks like:
129+
130+
```json
131+
{
132+
"object": "workspace.google_drive.access_policy",
133+
"allow_list": ["0AExampleFinanceDrive", "0AExampleResearchDrive"],
134+
"allow_personal_drive": false
135+
}
136+
```
137+
138+
The response field is `allow_list`; the request field is `drive_ids`.
139+
140+
## Prepare and replace an allowlist
141+
142+
Create `drives.csv` with a `drive_id` column:
143+
144+
```csv
145+
drive_id
146+
0AExampleFinanceDrive
147+
0AExampleResearchDrive
148+
```
149+
150+
Replace these sample IDs with actual shared-drive IDs. A CSV with a `drive_url`
151+
column also works; use exactly one of these two columns. A text file may contain
152+
one ID or root URL per line. Blank lines, text-file comments starting with `#`,
153+
and CSV rows without a value in the chosen column are ignored. An empty input
154+
cannot replace the policy; use the explicit `block-all` command for that intent.
155+
156+
Preview a replacement that also blocks My Drive:
157+
158+
```bash
159+
python3 google_drive_access_admin.py replace \
160+
--workspace-id "$WORKSPACE_ID" \
161+
--drives-file drives.csv \
162+
--my-drive block \
163+
--dry-run
164+
```
165+
166+
The preview prints the current and proposed policies, resolved drives, HTTP
167+
method, and request body. It reads the current policy and verifies any supplied
168+
URLs without writing. Review the result, then run the same command without
169+
`--dry-run` to send:
170+
171+
```http
172+
PUT /v1/manage/workspaces/<workspace-id>/google-drive/drive-access/allow-list
173+
Authorization: Bearer <CHATGPT_ADMIN_TOKEN>
174+
Content-Type: application/json
175+
176+
{
177+
"drive_ids": ["0AExampleFinanceDrive", "0AExampleResearchDrive"],
178+
"allow_personal_drive": false
179+
}
180+
```
181+
182+
This replaces the full shared-drive allowlist. Omit `--my-drive` to preserve its
183+
observed setting. You can supply repeated `--drive-id` or `--drive-url` options
184+
instead of a file, or combine them with a file.
185+
186+
## Add or remove selected drives
187+
188+
For an existing finite allowlist, the script reads it, computes the union or
189+
difference, and sends one replacement `PUT`:
190+
191+
```bash
192+
python3 google_drive_access_admin.py add \
193+
--workspace-id "$WORKSPACE_ID" \
194+
--drive-id 0AExampleOperationsDrive \
195+
--dry-run
196+
197+
python3 google_drive_access_admin.py remove \
198+
--workspace-id "$WORKSPACE_ID" \
199+
--drive-id 0AExampleFinanceDrive \
200+
--dry-run
201+
```
202+
203+
Remove `--dry-run` after review. Removing the final selected drive blocks every
204+
shared drive and requires `--yes`. Adding a duplicate or removing an absent ID
205+
leaves the policy unchanged, unless you also change My Drive.
206+
207+
When `allow_list` is `null`, both `add` and `remove` stop: all shared drives are
208+
already allowed, and the API has no exclusion-list operation. Use `replace` to
209+
select a finite set. To remove an inaccessible drive, supply its known ID
210+
directly; URL verification requires Google access to that drive.
211+
212+
## Change My Drive access
213+
214+
To change My Drive while carrying forward the observed shared-drive policy:
215+
216+
```bash
217+
python3 google_drive_access_admin.py set-my-drive \
218+
--workspace-id "$WORKSPACE_ID" \
219+
--my-drive block \
220+
--dry-run
221+
```
222+
223+
Review and remove `--dry-run` to apply. Use `--my-drive allow` to allow My Drive.
224+
The request includes the observed shared-drive IDs because `PUT` requires
225+
`drive_ids`, even when only My Drive is changing.
226+
227+
## Block or restore shared-drive access
228+
229+
Preview blocking all shared drives and My Drive together:
230+
231+
```bash
232+
python3 google_drive_access_admin.py block-all \
233+
--workspace-id "$WORKSPACE_ID" --my-drive block --dry-run
234+
```
235+
236+
To apply, replace `--dry-run` with `--yes`. Without `--my-drive`, this command
237+
blocks shared drives while preserving the observed My Drive setting.
238+
239+
To remove the shared-drive restriction, preview `reset`:
240+
241+
```bash
242+
python3 google_drive_access_admin.py reset \
243+
--workspace-id "$WORKSPACE_ID" --dry-run
244+
```
245+
246+
Replace `--dry-run` with `--yes` to send `DELETE`. Reset sets `allow_list` to
247+
`null` and preserves My Drive. It does not reset My Drive to its default.
248+
Commands whose proposed policy already matches the read policy skip the write.
249+
250+
## Coordinate writes and handle errors
251+
252+
Run one administrator's update at a time. This endpoint has no version
253+
precondition or guaranteed conflict detection. A concurrent save can overwrite
254+
changes to these controls or other Google Drive app settings. A dry run is a
255+
preview, not a reservation; applying the command reads the policy again.
256+
Keep the app's enabled/disabled state in mind: saving a policy does not enable
257+
a disabled Google Drive app.
258+
259+
The example retries `GET` requests up to twice after transient network errors or
260+
HTTP 429, 502, 503, or 504. It sends each `PUT` or `DELETE` only once and provides
261+
no idempotency-key option. After an ambiguous write failure, run `list`, inspect
262+
the current state, and reconcile your intended change before retrying.
263+
264+
| Error | What to check |
265+
| --- | --- |
266+
| HTTP 401 or 403 | Admin key, `chatgpt.enterprise.apps.write`, administrator permissions, workspace organization, and feature availability. For Google requests, check the Google token and drive access. |
267+
| HTTP 404 | Workspace ID or shared-drive ID. A folder ID is not a shared-drive ID. |
268+
| HTTP 400 | Request fields, ID format, and the 1,000-entry limit. |
269+
| HTTP 409 | Read the current policy and review conflicting changes before retrying. Concurrent saves do not always produce this error. |
270+
| HTTP 429 | Wait before retrying a write, then inspect the current policy. |
271+
| HTTP 503 | The Google Drive policy capability may be unavailable or a service may be temporarily unavailable. Confirm workspace support if it persists. |
272+
273+
## Run the offline tests
274+
275+
From the repository root:
276+
277+
```bash
278+
python3 -B -m unittest discover \
279+
-s examples/chatgpt/google_drive_access \
280+
-p 'test_*.py' -v
281+
```
282+
283+
The tests mock Google Drive and the ChatGPT Admin API. They require no
284+
credentials and make no live requests.

0 commit comments

Comments
 (0)