From 3254fbd273dd217aa1fe63bcba40aad81e561f49 Mon Sep 17 00:00:00 2001 From: Squire as Brandon High <759848+highb@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:12:15 +0000 Subject: [PATCH] fix(pat): surface issuance degradations at Warn and correct the docs note Both privilege degradations added in #149 logged at Debug, so at the default level an operator saw nothing distinguishing a locally computed expiry from one Snowflake actually returned. Per the repo's log-level rules a skip-and-continue degradation is Warn, and both fire once per issuance rather than per resource, so there is no volume concern. The read-back message now also carries the estimated expiry it substituted. The token builder's own skip in ListProgrammaticAccessTokens stays at Debug: that one fires once per user per sync and is the per-resource case the same rules keep quiet. The service-user note in the docs still claimed the connector verifies the default-role grant before issuing. That has been best-effort since #149, so it is softened to match. Co-authored-by: c1-squire-dev[bot] Co-Authored-By: Claude Opus 5 --- docs/connector.mdx | 2 +- pkg/connector/users.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/connector.mdx b/docs/connector.mdx index 73356e7d..b912e582 100644 --- a/docs/connector.mdx +++ b/docs/connector.mdx @@ -37,7 +37,7 @@ Token lifetime is set by the requester. Snowflake accepts whole days only, and t -**Service users must have a granted default role.** For a user of type `SERVICE`, `SERVICE_AGENT`, or `LEGACY_SERVICE`, the connector restricts the issued token to the user's `DEFAULT_ROLE` and verifies that role is actually granted to the user first. If the user has no default role, or the default role is not granted, issuance fails before any token is created. +**Service users must have a granted default role.** For a user of type `SERVICE`, `SERVICE_AGENT`, or `LEGACY_SERVICE`, the connector restricts the issued token to the user's `DEFAULT_ROLE`, and verifies that role is granted to the user when its own role can read the user's grants. If the user has no default role, issuance fails before any token is created. If the default role is not granted, issuance fails either on that check or on Snowflake's own rejection of the statement. diff --git a/pkg/connector/users.go b/pkg/connector/users.go index 10afa22a..83de0e1d 100644 --- a/pkg/connector/users.go +++ b/pkg/connector/users.go @@ -97,7 +97,7 @@ func (o *credentialUserBuilder) Issue(ctx context.Context, input *connectorbuild // SHOW GRANTS TO USER needs privileges that creating the token does not. // This check only turns a Snowflake rejection into a better message, so a // role that cannot run it must still be allowed to issue. - ctxzap.Extract(ctx).Debug("baton-snowflake: skipping default-role check: insufficient privileges", + ctxzap.Extract(ctx).Warn("baton-snowflake: skipping default-role check: insufficient privileges", zap.String("username", input.IdentityID.Resource)) case err != nil: return nil, fmt.Errorf("baton-snowflake: verify service user's default role: %w", err) @@ -167,8 +167,9 @@ func (o *credentialUserBuilder) Issue(ctx context.Context, input *connectorbuild // locally computed expiry is never later than Snowflake's, so reporting it // errs towards early rotation rather than towards a credential that outlives // what C1 believes. - ctxzap.Extract(ctx).Debug("baton-snowflake: cannot read back token expiry: insufficient privileges", - zap.String("username", input.IdentityID.Resource)) + ctxzap.Extract(ctx).Warn("baton-snowflake: reporting a locally computed token expiry: insufficient privileges to read it back", + zap.String("username", input.IdentityID.Resource), + zap.Time("estimated_expires_at", expiresAt)) default: return nil, fmt.Errorf("baton-snowflake: read created programmatic access token expiry: %w", err) }