From fa581159be79395ec7749e14f3986fce1d322b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Sun, 12 Jul 2026 19:42:04 +0200 Subject: [PATCH] List Google Analytics subproperties in access reviews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit listProperties filtered properties with parent:accounts/{id}, which returns only properties whose direct parent is the account and silently drops subproperties and roll-up properties (parented to another property). A member holding a binding only on such a subproperty was omitted from the review. Switch to the ancestor:accounts/{id} filter, which walks the whole account hierarchy and is a strict superset, so no property is lost. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/accessreview/drivers/google_analytics.go | 9 ++++++--- pkg/accessreview/drivers/testdata/google_analytics.yaml | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/accessreview/drivers/google_analytics.go b/pkg/accessreview/drivers/google_analytics.go index 6443db12a..086bf3689 100644 --- a/pkg/accessreview/drivers/google_analytics.go +++ b/pkg/accessreview/drivers/google_analytics.go @@ -143,13 +143,16 @@ func (d *GoogleAnalyticsDriver) collectBindings(ctx context.Context, members map return fmt.Errorf("cannot list all google analytics access bindings: %w", ErrPaginationLimitReached) } -// listProperties returns the numeric IDs of every property directly beneath the -// account. +// listProperties returns the numeric IDs of every property under the account, +// including subproperties and roll-up properties. The ancestor filter walks the +// whole account hierarchy (parent: would return only properties whose direct +// parent is the account, silently dropping subproperties parented to another +// property, and with them any subproperty-only members). func (d *GoogleAnalyticsDriver) listProperties(ctx context.Context) ([]string, error) { var propertyIDs []string pageToken := "" - filter := url.Values{"filter": {"parent:accounts/" + d.accountID}} + filter := url.Values{"filter": {"ancestor:accounts/" + d.accountID}} for range maxPaginationPages { endpoint, err := googleAnalyticsURL(pageToken, filter, "v1alpha", "properties") diff --git a/pkg/accessreview/drivers/testdata/google_analytics.yaml b/pkg/accessreview/drivers/testdata/google_analytics.yaml index a375111d7..744e9fd32 100644 --- a/pkg/accessreview/drivers/testdata/google_analytics.yaml +++ b/pkg/accessreview/drivers/testdata/google_analytics.yaml @@ -45,13 +45,13 @@ interactions: host: analyticsadmin.googleapis.com form: filter: - - parent:accounts/123456 + - ancestor:accounts/123456 pageSize: - "200" headers: Accept: - application/json - url: https://analyticsadmin.googleapis.com/v1alpha/properties?filter=parent%3Aaccounts%2F123456&pageSize=200 + url: https://analyticsadmin.googleapis.com/v1alpha/properties?filter=ancestor%3Aaccounts%2F123456&pageSize=200 method: GET response: proto: HTTP/2.0