GoogleCloudPlatform / GoogleCloudPlatform/firebase-extensions
bigquery-firestore-export: serviceAccountName is nested inside transferConfig, so the scheduled query's run-as account is never set
- Dominant language
- TypeScript
- Stars
- 124
- Forks
- 67
- Avg merge
- 5d 5h
- Merged PRs (30d)
- 5
Description
### [READ] Step 1: Are you in the right place?
Yes, this is a bug in a specific extension in this repository: `bigquery-firestore-export`.
### [REQUIRED] Step 2: Describe your configuration
- Extension name: **bigquery-firestore-export**
- Extension version: **0.2.2**
- Configuration values: not configuration dependent. Reproduced against the BigQuery Data Transfer API directly, because the extension's own fallback hides the difference (see below).
### [REQUIRED] Step 3: Describe the problem
`serviceAccountName` is a field of `CreateTransferConfigRequest` and `UpdateTransferConfigRequest`. It is not a field of `TransferConfig`. `functions/src/dts.ts` builds the account email and spreads it into the nested `transferConfig` object, so it is dropped before the request is sent. It does not fail the type check, because a spread is not subject to excess property checking against `ITransferConfig`.
This is masked today. With no owner specified, Data Transfer assigns the caller, and the caller is the same `ext-{instanceId}@` account that `functions/src/index.ts` passes in, so a normal install ends up with the intended owner and nothing looks wrong.
Two consequences:
1. Ownership is only ever inherited, never set. If the owning principal is deleted, every run fails, and creating a new account at the same email does not restore it, since the owner is bound to the account's internal ID.
2. There is no repair path. `constructUpdateTransferConfigRequest` omits the field, per the comments in `functions/src/dts.ts` and `functions/src/index.ts` stating that it cannot be updated on an existing config. In testing it can be: `serviceAccountName` on the update request plus `service_account_name` in the update mask re-points an existing config, and the next run succeeds.
#### Steps to reproduce:
Run against the Data Transfer API, naming an account other than the caller:
1. Create a scheduled query with `serviceAccountName` nested inside `transferConfig`, as the extension does. `ownerInfo` comes back as the caller. The named account is never applied.
2. Create the same config with `serviceAccountName` on the request instead. `ownerInfo` is the named account.
3. Delete the owning account. The next run fails with `code 16: The transfer user is not valid`.
4. Recreate an account at the same email and re-grant its roles. Runs still fail.
5. Update with `serviceAccountName` on the request and `service_account_name` in the update mask. Ownership is restored and the next run succeeds.
##### Expected result
The account passed in `functions/src/index.ts` is applied as the scheduled query's run-as account, and can be re-pointed on a later update.
##### Actual result
The field is dropped. Ownership always falls back to the caller and cannot be changed afterwards.
#### Note on fixing
Moving the field onto the request makes the create call require `iam.serviceAccounts.actAs` on the named account, even when that account is the caller itself:
```
7 PERMISSION_DENIED: Requesting user ...@... does not have
iam.serviceAccounts.actAs permission to act as service account ...@...
```
`extension.yaml` currently requests `datastore.user`, `bigquery.admin` and `pubsub.admin`, and no IAM role, so a fix likely needs `roles/iam.serviceAccountUser`.
#### Scope of what was verified
The steps above were run directly against the Data Transfer API in a test project. The misplaced field, the omission on the update path, and the requested roles are from reading 0.2.2. Whether any installed instance has actually lost its owning account was not investigated.
Contributor guide
Research direction
Read functions/src/dts.ts and functions/src/index.ts first, focusing on how serviceAccountName is passed to the create and update requests. Then inspect extension.yaml for the requested IAM roles. Done means the named account is applied on creation, can be restored through the update path, and the required permissions are documented or requested.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- firebase, google-cloud, typescript
- Domain
- api, backend, cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100