Repository navigation
Conversation
…o team documents arrive with their own identity and dates Signed-off-by: muemich <michaelmuellerrw@gmail.com>
WaylandYang
left a comment
There was a problem hiding this comment.
Thank you for this, and for opening the issue with the use case spelled out. Yes, we want a Google Drive source, and the design reads well: read-only scope, the token never in the browser, PKCE with a single-use state bound to the session that started it, and Drive's own markers for change detection.
Two things before we review it in detail:
- Please split it the way you offered. First the ADR, the migration, the connector and the routes; then the web panel. Each part is then small enough to review properly, and the second can follow quickly.
- The SQL needs tests that run in CI. None of the current tests touch the database, so the flow table, the markers and the reconciliation are covered only by your manual run. Most of all, the callback's checks deserve database-backed tests: a state used twice, a callback from another session, a person who is no longer an editor, and an expired flow. CI runs every test with a migrated PostgreSQL (
UTOPIA_TEST_REQUIRE_DB=1);crates/utopia-server/src/source_checkpoint_tests.rsshows the fixture.
Because this adds an OAuth entry point, we will do a security-focused pass on the first part once it is up.
Thanks again; this is a careful piece of work, and a connector people have asked for.
WaylandYang
left a comment
There was a problem hiding this comment.
Thank you again. I read the connector, the routes and the sync with an eye on the new OAuth entry point, so these can go into the first part when you split. The flow itself is right: PKCE, a single-use state deleted before use, the callback bound to the session that started it, the editor check repeated at the callback, and folder ids checked against a strict character set before they reach a Drive query.
Two things need changing, both about whose Drive is read:
- Only the person who connected should choose what is read from their Drive. Today any editor of the knowledge base can browse the connected account's folders (
GET …/folders?parent=…) and select any of them (PUT …/folders), so a colleague can pull folders the connecting person never chose into a base everyone in it can read. The same grant keeps syncing after that person leaves the base. Please record who connected (connected_by), allow only them to browse and select, and have a sync stop with a "reconnect" error once they are no longer an editor of the base. - A walk that stops at the cap never reaches the rest. The walk stops at 2,000 listed files, in the same order every time, and the run still finishes as
ok, so a folder tree larger than that never brings in its tail (the same kind of gap as #1212). Unchanged files cost nothing thanks to the markers, so the cap is better placed on downloads than on the listing, and a run that stops early should say so rather than report success.
Smaller ones:
- One editor can fill the 1,000 pending flows and block every connection on the server for ten minutes; a cap per user avoids that.
callback,selectanddisconnectreadconfig, change it and write it back, so two of them at once can drop the other's change (a folder selection can overwrite a token just saved). Updating only the keys involved in one statement, or locking the row, avoids it.
The database-backed tests from my earlier note are the place to pin these down: the second state use, another session, an editor removed between start and callback, and a non-connecting editor trying to browse.
Thanks again; with these the first part should be close.
Closes #1223.
Why
Most teams keep their documents in Google Drive. Today the only way into Utopia is to export them by hand or to copy them into a bucket, which loses each file's identity and its modification time. Drive passes the four tests of 0013. But no administrator key covers a person's own files, so this connector is the first that a person connects with their own account. The reasoning, including what was rejected (a service account, the Google Picker, the changes feed), is in ADR 0068.
What changes
google_drive. An editor creates the source, clicks Connect Google Drive (an OAuth popup asking fordrive.readonlyplusopenid email), then picks folders in a folder browser that covers My Drive, Shared with me and every shared drive. Subfolders are included.refresh_tokenis added toSOURCE_SECRET_KEYS. The browser never holds a Google token; the folder browser is served by the server.google_drive_flows, migration 0112) uses PKCE and a single-use state. The callback must arrive in the same session that started the flow, and the user must still be an editor. Without that check, someone could have another person click through consent and attach that person's Drive to their own source.doc_timeismodifiedTime.google_drive_files):md5Checksumfor binary files,modifiedTimefor Google formats. An export of an unchanged Doc is a new zip every time, so comparing content hashes would re-extract every Doc on every run.GET /google-drive/status,GET /google-drive/callback, and under/kbs/{id}/sources/{source_id}/google-drive/:POST connect,DELETE connection(also revokes the token at Google),GET folders,PUT folders.UTOPIA_GOOGLE_DRIVE_CLIENT_ID,_CLIENT_SECRETand_REDIRECT_URI(documented in.env.example). When they are not set, the kind cannot be created.Size. About 1,100 lines of Rust (roughly half of it comments, in the house style), 460 lines of tests, and 380 lines of web. I kept it as one PR because the backend can't be exercised without the connect-and-pick flow, and the UI is useless without the backend. If you'd rather review it in two parts (ADR, migration, connector and routes first, then the web panel), say so and I'll split it.
How it was checked
devat c278cb8.cargo fmt --all --checkandcargo clippy --workspace --all-targets -- -D warningsare clean.cargo test --workspacepasses, including 12 new tests ingoogle_drive_tests.rsthat use wiremock. They cover:exportSizeLimitExceededreason in the errorpnpm build(which includes the design lint) andpnpm test(268 tests) pass.it_reads_from_a_real_driveruns only when given a client and a refresh token.Before review
git commit -s)cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warningsandcargo test --workspacepasscrates/utopia-store/was tested withUTOPIA_DATABASE_URLset (n/a: the new SQL lives inutopia-server; exercised by the manual run above)web/:pnpm buildandpnpm testpassdev, andCURRENT_SCHEMA_VERSIONincrates/utopia-cli/src/main.rsequals the number of files inmigrations/web/src/i18n/en.tsandzh.tsdocs/decisions/