Google Drive
Proxy for the Google Drive v3 REST API. Ships with a tenant-filled deny template — the connector author knows the shape of Drive, the operator fills in the specifics (which tags are sensitive; folder/label-based governance is configured through the shared file access control below).
Overview
Drive is the first dual-auth connector. Pick:
idp_passthroughwhen the caller is a real human and the agent should act as them — reads and writes apply to the user's own Drive, respecting every share / membership the user already has. Requires the operator to register Google as an IdP on the AS.google_sawhen the caller is an unattended service (CI job, automation agent) and the operator owns the data. Uses a Google service account with domain-wide delegation impersonating whichever Workspace user was stamped into the managed-resource setup.
A single managed resource binds to one auth mode. To serve both
shapes of caller, create two managed resources (e.g. drive-user and
drive-sa). The policy can key off input.resource.name to apply
different obligations.
Default policy
This connector ships no Rego package. policy/drive.rego, its test, and
policy/data.json are gone, and the manifest no longer declares a policy
block. Everything Drive enforces comes from shared controls — see File access
control below.
1. IdP routing (shared control)
Not a rule in this connector. When a resource runs in idp_passthrough mode,
the general pbac.controls.idp_passthrough control sends a caller who holds no
forwardable Google token to Google first: /authorize redirects them, /token drops this
connector's resource, and /introspect tells the gateway which IdP the call
needs.
Read-only files by tag — removed, not moved
A sensitive_tags deny rule used to be documented here: files carrying an
operator-flagged custom-property tag were read-only. It never worked. It gated
on input.action.method and input.resource.tags, neither of which the gateway
sends to the PDP, so it was inert on every instance that ever ran it, while the
connector's operator_data_schema advertised sensitive_tags as configurable.
The rule and the setting are both gone. Nothing enforces less as a result — nothing was enforcing. The capability was not rebuilt: unlike folder and label governance below, tag-valued read-only had no home in the general file-access control (Drive custom properties are a different API surface from Drive Labels), and it was invented rather than asked for.
Folder-based blocking used to live here too, as a
blocked_foldersdeny rule keyed oninput.resource.parent_folder_id. That rule has been retired — folder governance is now alocationsource on the shared file access control (see below), which also closes the direct-fetch gap the old rule had (it only ever covered Drive's own routes).
Operator data
This connector declares no operator settings of its own. Governance comes from
the shared controls: file access below, and the general controls in
pbac.controls.* (read-only mode, capability opt-in, IdP alignment).
File access control
list_files/get_file results are governed by the shared file access
control — one general mechanism
(typed label and location sources, one pooled predicate) used by every
governed Google file connector (Drive, Docs, Sheets, Slides), plus
auto-labelling of files an agent creates so the same gate governs them. All
governing sources pool into a single OR-of-approvals, deny-wins decision:
a file is returnable if it carries an approved label or sits in an
approved folder, and withheld if it carries a restricted label or lives in
a blocked folder. Full design — the pooled predicate, fail-closed rules, and
the two enforcement points — lives in the File Access Control
guide;
for getting label/folder/drive IDs and the required OAuth scopes, see the
Google file access setup
guide.
This is response-side suppression on the inline path: the file is fetched from Drive and then pruned/denied before it reaches the agent — not a "never fetched" control.
Label and folder/location governance (shared control)
Neither label nor folder/location governance is configured under this
connector's own operator key anymore. The old Drive-only label_filter,
location_filter, and blocked_folders keys have all been removed —
any config still setting them is silently ignored, so it gets zero
enforcement. Both now go through the single shared control,
pbac.operator.controls.file_access, the same mechanism Docs, Sheets, and
Slides use. Drive's projection runs in inline mode — label/parent/drive
metadata comes back on the list_files/get_file response itself, so the
gateway filters/denies from that response directly instead of a separate
resolve round trip — but the operator-facing configuration is identical to
every other governed connector:
curl -X PUT http://localhost:8080/admin/policy/data/pbac/operator/controls/file_access \
-H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" \
-d '{
"sources": {
"drive-labels": {
"type": "label",
"required": ["approved-for-ai"],
"blocked": ["restricted"]
},
"drive-location": {
"type": "location",
"required": ["<approved-folder-id>"],
"blocked": ["<hr-folder-id>"]
}
},
"governed": {
"urn:connector:identos:google-drive": ["drive-labels", "drive-location"]
}
}'
- Under
sources, declare a policy per source:type(labelorlocation),required(allowlist, any-of) andblocked(denylist, always wins). Approving a folder — making the folder itself and its direct contents returnable regardless of their own labels — is alocationsource with the folder ID inrequired; blocking a folder ID hides the folder itself and its direct contents (deny-wins). Alocationsource matches an item by its own ID as well as its parent container(s), which is what lets the folder itself surface (or be hidden). Google Drive does not allow labels on folders, so folder governance is always by ID via alocationsource, never by a folder label (and there is no label inheritance). - Under
governed, list (as an array) which sources govern this connector's resource type. All listed sources pool into one OR-of-approvals, deny-wins decision — a file is returnable if it matches any source'srequired, and withheld if it matches any source'sblocked. The deciding rule (blocked_match/not_in_required) shows up in the audit reason. Omitting the connector leaves it ungoverned (inert; no check). - Folders and shared drives on a
locationsource are configured by ID, not path — Drive has no path metadata. Location matching is own-ID + direct-parent + shared-drive only (one level, no tree walking); for a whole-subtree boundary (e.g. an entire department), put the tree in a dedicated shared drive and match on the drive's ID instead of walking folders.
Drive Labels were chosen over appProperties because they're visible/editable
in the Drive UI and admin-governed — appProperties is invisible and
freely rewritable by anything with write access, so it isn't a real control.
Folder viewability (allow_containers)
A Drive folder cannot carry a label, so the moment a label source has a
required allowlist every folder is pruned out of list_files — an agent
cannot browse or search because it never sees a folder to look inside.
Naming each folder in a location source's required fixes that folder;
allow_containers fixes it in general:
{
"sources": { "drive-labels": { "type": "label", "required": ["approved-for-ai"] } },
"governed": { "urn:connector:identos:google-drive": ["drive-labels"] },
"allow_containers": { "urn:connector:identos:google-drive": true }
}
Folders become returnable; the files inside them still need their own
approval. A folder ID on any source's blocked list stays hidden, together
with its direct contents — the exemption answers the not_in_required
objection only, never blocked_match. Reads only (it rides on the inline
filter), and off by default: folder names are metadata an operator may not
want enumerated.
The connector half is file_governance.container in this manifest —
mimeType = application/vnd.google-apps.folder. mimeType must stay in
both read routes' pinned query_inject.fields, or the test can never match and
folders stay hidden while the config reads as enabled; ConnectorLinter
enforces that coupling.
Write-side default labelling (now part of the shared control)
Auto-labelling agent-created files is now a feature of the shared
file_access control, not a per-connector key: give a label source a
default and newly-created files are stamped with it (so an agent can read back
what it makes). It's connector-agnostic — the same config governs Drive, Docs,
Sheets, and Slides — with the write performed by the google-drive-modify-labels
applier (shared-drive-aware). Configure it on the label source under
pbac.operator.controls.file_access:
"sources": {
"org-labels": {
"type": "label", "required": ["<approved-label-id>"], "blocked": [],
"default": { "label_id": "<approved-label-id>", "field_id": "<field-id>", "value": "general" }
}
}
See the File Access Control guide
and the design spec for the general model. The per-connector
create_default_label key and the connector rego rule that read it are
gone: that rule emitted a flat {label_id, field_id, value} payload, which
ApplyLabelAction stopped understanding when it became a dispatcher keyed on
applier — so it had been silently skipping. Move the same payload to the
label source's default, as above.
Gating note: the create-time apply_label is gated on the route's scope
(input.context.rs_context.scopes, drive:files:write), not
input.action.method — which is never populated for connector policy on the
real gateway path.
Reading comments
list_comments returns the comment threads on a file: each comment's text,
author, whether it is resolved, its replies, and the text it is anchored to
(quotedFileContent). Drive keeps comments on its own API, so comments on Docs,
Sheets and Slides files are all read through this one tool, by file ID.
It needs drive:comments:read, a scope of its own that nothing grants by
default. That is deliberate: a comments response is a list of comments, not
files, so the file access control above has no labels or folders to evaluate and
does not filter it. A client holding the scope can read the comments on any file
the user can open, including files the control would hide from list_files.
Grant it only where that is acceptable.
Drive rejects a comments call that names no fields, so the route pins a default set. A caller may add fields to it but not drop any.
Writing comments
get_comment reads one thread (drive:comments:read). Four tools write, all behind
drive:comments:write, which nothing grants by default: create_comment,
reply_to_comment (with action resolve or reopen to close or reopen a thread),
update_comment and delete_comment. Comments are visible to,
and notify, everyone with access to the file. Google lets a user edit or delete only their
own comments. Google's editors (Docs, Sheets and Slides) do not render a comment created
through the Drive API anchored to content; they show it as an unanchored comment. So
create_comment makes a general comment on the file, and it cannot be pinned to text, a
slide element or a cell.
Writing comments needs the drive or drive.file upstream scope. This connector asks
for drive.file, which Google limits to files the app created or the user opened with
it, so writes to other files can fail with 403 until the full drive scope is added.
Scope model
Two vocabularies are at play:
| Scope | Used for | |
|---|---|---|
| PBAC scopes (internal) | drive:files:read, drive:files:write, drive:admin, drive:comments:read, drive:comments:write | Route authorization inside PBAC — the PDP decides which MCP tools the caller can invoke |
| Upstream OAuth scopes | https://www.googleapis.com/auth/drive.readonly, https://www.googleapis.com/auth/drive.file, https://www.googleapis.com/auth/drive.labels.readonly, https://www.googleapis.com/auth/drive.labels | What the IdP (Google) must grant in the user's access token for idp_passthrough to work |
drive.labels (write) is required for the apply_label follow-up to call
files.modifyLabels. drive.labels.readonly is required
for labelInfo to appear on list_files/get_file responses so the shared
file access control's filter_file_access obligation can enforce a label
source; drive.readonly already covers the parents/driveId fields a
location source reads (the standalone drive.metadata.readonly scope is
only needed when no Drive connector is installed and the control has to
resolve those fields via a direct API call instead — see the Google file
access setup guide).
When this connector is provisioned in idp_passthrough mode, the gateway
automatically merges its declared upstream scopes into the Google IdP's
scopes list. Users who authenticated before the connector was
installed will see "stored IdP token missing required scopes" until they
re-log at Google.
See Google Workspace: multi-connector setup for how this works across drive + gmail + calendar sharing a single Google IdP.
Manifest reference
- ID:
identos.google-drive - Version:
1.6.0 - Resource type:
urn:connector:identos:google-drive
Supported auth modes
| Type | Details |
|---|---|
idp_passthrough | requires IdP google |
google_sa | setup fields: sa_key_env, domain |
Setup fields
| ID | Label | Default | Secret? | Notes |
|---|---|---|---|---|
base_url | API base URL | https://www.googleapis.com | no | — |
upstream_auth.type | Authentication | idp_passthrough | no | idp_passthrough forwards each user's own Google OAuth token (recommended). google_sa uses a Workspace service account with domain-wide delegation impersonating a fixed user. |
sa_key_env | Service account key | — | yes | Pick a secret containing the service-account JSON. Required only for the google_sa auth mode. / shown when upstream_auth.type == 'google_sa' |
domain | Workspace domain | — | no | placeholder: example.com / Required only for the google_sa auth mode. / shown when upstream_auth.type == 'google_sa' |
Scopes
| Scope |
|---|
drive:files:read |
drive:files:write |
drive:admin |
drive:comments:read |
drive:comments:write |
Routes
| Method | Pattern | Scope | Resource template |
|---|---|---|---|
GET | /drive/v3/files | drive:files:read | — |
GET | /drive/v3/files/{file_id} | drive:files:read | drive://{{file_id}} |
GET | /drive/v3/files/{file_id}/permissions | drive:admin | drive://{{file_id}} |
GET | /drive/v3/files/{file_id}/comments | drive:comments:read | drive://{{file_id}} |
GET | /drive/v3/files/{file_id}/comments/{comment_id} | drive:comments:read | drive://{{file_id}} |
POST | /drive/v3/files/{file_id}/comments | drive:comments:write | drive://{{file_id}} |
POST | /drive/v3/files/{file_id}/comments/{comment_id}/replies | drive:comments:write | drive://{{file_id}} |
PATCH | /drive/v3/files/{file_id}/comments/{comment_id} | drive:comments:write | drive://{{file_id}} |
DELETE | /drive/v3/files/{file_id}/comments/{comment_id} | drive:comments:write | drive://{{file_id}} |
POST | /drive/v3/files | drive:files:write | — |
PATCH | /drive/v3/files/{file_id} | drive:files:write | drive://{{file_id}} |
DELETE | /drive/v3/files/{file_id} | drive:admin | drive://{{file_id}} |
MCP tools
| Name | Scope | Description |
|---|---|---|
list_files | drive:files:read | List files in Google Drive. Supports standard Drive query syntax for filtering (e.g. "name contains 'report'" or "mimeType = 'application/pdf'"). |
get_file | drive:files:read | Get metadata for a single Drive file by its file ID. |
list_file_permissions | drive:admin | List the sharing permissions on a Drive file. |
list_comments | drive:comments:read | List the comment threads on a Drive file, including Docs, Sheets and Slides files: each comment's text, author, resolved state, replies, and the text it is anchored to (quotedFileContent). |
get_comment | drive:comments:read | Get a single comment thread by ID: text, author, resolved state, replies and the text it is anchored to. |
create_comment | drive:comments:write | Add a new top-level comment to a Doc, Sheet, Slides or other Drive file. It is visible to everyone with access and notifies them. It is a general comment on the file: Google's editors (Docs, Sheets and Slides) do not show a comment created through the Drive API anchored to content, so it cannot be pinned to text, a slide element or a cell. |
reply_to_comment | drive:comments:write | Reply to a comment thread. Set action to resolve to mark the thread resolved, or reopen to reopen it; Google needs content only for a plain reply, but a resolve with a short note is clearer to the thread's readers. |
update_comment | drive:comments:write | Replace the text of a comment. Google only lets a user edit their own comments. |
delete_comment | drive:comments:write | Permanently delete a comment and its replies. Irreversible. Google only lets a user delete their own comments. |
create_file_metadata | drive:files:write | Create a Drive file's metadata record (no binary content) — a folder, placeholder doc, or shortcut. Pass mimeType application/vnd.google-apps.folder for a folder or application/vnd.google-apps.document for an empty Doc, and parents to create it inside a folder. For binary uploads, use the Drive web UI or a dedicated upload client. |
update_file_metadata | drive:files:write | Update a Drive file's metadata (name, description, starred flag) and/or move it between folders. To move a file, pass addParents (folder IDs to add) and removeParents (folder IDs to remove). |
delete_file | drive:admin | Permanently delete a Drive file. Irreversible — prefer trashing via update_file_metadata when possible. |