Skip to content

Add support for setting passwords - #77

Merged
ggreer merged 10 commits into
mainfrom
ggreer/password
Sep 27, 2025
Merged

Add support for setting passwords#77
ggreer merged 10 commits into
mainfrom
ggreer/password

Conversation

@ggreer

@ggreer ggreer commented Sep 26, 2025

Copy link
Copy Markdown
Contributor

Description

  • Upgrade baton-sdk. Start to hook up CI tests again.

  • Add support for setting passwords.

  • Bug fix

  • New feature

Useful links:

Summary by CodeRabbit

  • New Features
    • Credential management expanded: account creation and rotation support local credential options, encrypted/plaintext password handling, rotation capability details, a Rotate operation, and plaintext outputs for generated passwords.
  • Chores
    • Upgraded Go runtime to 1.25; updated baton SDK and added an indirect semver dependency.
  • Notes
    • Added a public error for missing rotation configuration; intended to remain backward-compatible.

Note

Upgrade to baton-sdk v0.4.x and add encrypted/plaintext password support across account create/rotate, plus session cache and sync enhancements.

  • Auth/Credentials:
    • Replace CredentialOptions flows with LocalCredentialOptions; support random, plaintext, and encrypted passwords (client-secret decryption) for account create/rotate.
    • New crypto helpers (client secret parsing, option conversion, password gen) and capability detail plumbing.
  • Sync/Runtime:
    • Add session cache (in-memory and gRPC) with context propagation via new gRPC interceptors; clear on cleanup.
    • Propagate ActiveSyncId through requests; introduce sync types (full, partial, resources_only) and option to skip entitlements/grants.
    • Improve cycle detection using SCC and compactors; stats/grant stats and .c1z decoder/options tweaks.
  • CLI/Lambda:
    • Wire session cache creation; DPoP client refactor; pass client-secret in context.
  • Upgrades:
    • Bump baton-sdk to v0.4.x and Go to 1.25; add semver dependency.

Written by Cursor Bugbot for commit c187ca3. This will update automatically on new commits. Configure here.

@coderabbitai

coderabbitai Bot commented Sep 26, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds credential-rotation configuration and encrypted-password support; centralizes password generation into a new generatePassword helper; replaces CredentialOptions with LocalCredentialOptions across APIs and tests; adds Rotate APIs and rotation-related helpers on userSyncer; and bumps Go/tool and dependencies in go.mod.

Changes

Cohort / File(s) Summary of Changes
Build & Dependencies
go.mod
Updated go directive 1.24 → 1.25; upgraded github.com/conductorone/baton-sdk v0.3.48 → v0.4.5; added indirect github.com/Masterminds/semver/v3 v3.4.0.
Account config
pkg/bsql/config.go
Added CredentialRotation field to ResourceType; added EncryptedPassword field to AccountCredentials; introduced EncryptedPasswordConfig and CredentialRotation types; added ExtractCredentialRotation() method.
Password helpers & tests
pkg/bsql/helpers.go, pkg/bsql/helpers_test.go
Removed legacy generateCredentials and related imports; tests switched from v2.CredentialOptions to v2.LocalCredentialOptions, added plaintext password case, and updated to call the new password generation path using test contexts (ctx := t.Context()).
Provisioning changes
pkg/bsql/provisioning.go
prepareQueryInputs now accepts context.Context and *v2.LocalCredentialOptions; added generatePassword(ctx, ...); collects/returns plaintext data list; added extractAndValidateCredentialRotation(); updated imports and error handling; moved password generation into helper.
User syncer & tests
pkg/bsql/user_syncer.go, pkg/bsql/user_syncer_test.go
Added interface conformance assertions; CreateAccount signature changed to accept *v2.LocalCredentialOptions; added Rotate and RotateCapabilityDetails methods and optionsFromCredentials helper; tests updated to use contexts and LocalCredentialOptions.
Errors / Resource types
pkg/bsql/resource_types.go
Added exported error ErrNoCredentialRotationDefined.

Sequence Diagram(s)

sequenceDiagram
    participant US as userSyncer
    participant GP as generatePassword
    participant PQ as prepareQueryInputs
    participant DB as DB/Provisioner

    rect rgba(230,248,255,0.6)
    Note over US,GP: CreateAccount / Rotate -> password generation
    US->>GP: generatePassword(ctx, LocalCredentialOptions)
    GP-->>US: password or error
    end

    rect rgba(240,250,230,0.6)
    Note over US,PQ: Prepare inputs & collect plaintext
    US->>PQ: prepareQueryInputs(ctx, provisioningCfg, accountInfo, LocalCredentialOptions)
    PQ-->>US: queryInputs + plaintextDataList
    end

    rect rgba(255,240,230,0.6)
    Note over US,DB: Execute provisioning (transactional)
    US->>DB: execute provisioning queries (txn)
    DB-->>US: success / failure
    US-->>Caller: plaintextDataList, annotations, error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I twitch my whiskers at each new line,
A password sprouts where configs align.
I hop to Rotate, I hop to Prepare,
Hidden keys gleam in encrypted lair.
Nibble the changelog, then bound off—harey and fair. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Add support for setting passwords” succinctly captures the primary new feature of the changeset—enabling password handling in account provisioning and rotation—without extraneous details. It is clear, specific to the main functionality added, and concise enough for teammates to understand the core update at a glance.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ggreer/password

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9b966d and c187ca3.

📒 Files selected for processing (2)
  • pkg/bsql/provisioning.go (5 hunks)
  • pkg/bsql/user_syncer.go (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/bsql/provisioning.go (3)
pkg/bsql/config.go (2)
  • AccountProvisioning (327-336)
  • CredentialRotation (396-401)
pkg/bsql/sql_syncer.go (1)
  • SQLSyncer (20-27)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
pkg/bsql/user_syncer.go (2)
pkg/bsql/config.go (1)
  • AccountCredentials (348-352)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (3)
pkg/bsql/provisioning.go (1)

284-291: Restore encrypted password support in generatePassword.

Line 285 only whitelists random and plaintext credential options, so selecting an encrypted password now fails with “unsupported credential options”. That regresses the advertised encrypted-password capability introduced by this PR. Please include the encrypted variant in the switch.

-	case *v2.LocalCredentialOptions_RandomPassword_, *v2.LocalCredentialOptions_PlaintextPassword_:
+	case *v2.LocalCredentialOptions_RandomPassword_,
+		*v2.LocalCredentialOptions_PlaintextPassword_,
+		*v2.LocalCredentialOptions_EncryptedPassword_:
pkg/bsql/user_syncer.go (2)

162-191: Guard against nil rotation configs before dereferencing.

extractAndValidateCredentialRotation returns ( "", nil, nil ) when rotation isn’t configured. Line 189 dereferences rotationConfig.Update, which will panic in that case. Add the same short-circuit used elsewhere before accessing the struct.

 	resourceTypeID, rotationConfig, err := s.extractAndValidateCredentialRotation()
 	if err != nil {
 		return nil, nil, err
 	}
+	if rotationConfig == nil {
+		return nil, nil, ErrNoCredentialRotationDefined
+	}

171-193: Expose the credentials namespace during rotation.

We build credentials["password"], but never insert it into queryInputs. Any rotation CEL expressions referencing credentials.password will now fail resolution. Mirror the create path by namespacing the credentials map.

 	if password != nil {
 		queryInputs["password"] = *password
 		credentials["password"] = *password
@@
 		}
 		plaintextDataList = append(plaintextDataList, passwordData)
 	}
+	if len(credentials) > 0 {
+		queryInputs["credentials"] = credentials
+	}

Comment @coderabbitai help to get the list of available commands and usage tips.

cursor[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
pkg/bsql/config.go (1)

375-380: JSON/YAML tag mismatch on AccountValidationConfig.Query

json tag should be "query", not "queries". Current tag will break JSON unmarshalling.

Apply this diff:

 type AccountValidationConfig struct {
   // Vars provides variables that can be used within account validation SQL queries.
   Vars map[string]string `yaml:"vars,omitempty" json:"vars,omitempty"`
   // Queries is a list of SQL statements to execute for account validation.
-  Query string `yaml:"query" json:"queries"`
+  Query string `yaml:"query" json:"query"`
 }
pkg/bsql/provisioning.go (1)

33-37: Dynamic entitlements: missing ID match may return wrong provisioning config

You return the first dynamic entitlement with Provisioning enabled without checking the entitlementID. This can pick the wrong config.

Apply this diff:

-  for _, e := range s.config.Entitlements.Map {
-    if e.Provisioning != nil {
+  for _, e := range s.config.Entitlements.Map {
+    if e.Id != entitlementID {
+      continue
+    }
+    if e.Provisioning != nil {
       l.Info("provisioning is enabled for entitlement", zap.String("entitlement_id", entitlementID))
       return e.Provisioning, true
     }
   }
pkg/bsql/user_syncer.go (1)

61-94: Capability details omit PlaintextPassword

Support and preferred sets only include NoPassword and RandomPassword. Add PlaintextPassword to reflect the new flow and config (after renaming in config.go).

Apply this diff:

   if accountProvisioning.Credentials.RandomPassword != nil {
     supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_RANDOM_PASSWORD)
     if accountProvisioning.Credentials.RandomPassword.Preferred {
       preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_RANDOM_PASSWORD)
     }
   }
+  // Plaintext password (user-supplied)
+  if accountProvisioning.Credentials.PlaintextPassword != nil {
+    supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD)
+    if accountProvisioning.Credentials.PlaintextPassword.Preferred {
+      preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD)
+    }
+  }
🧹 Nitpick comments (2)
pkg/bsql/user_syncer_test.go (1)

129-135: Consider adding a NoPassword case

Add a test where credentialOptions is NoPassword to assert no "password" key is injected and no plaintext data is produced.

Would you like me to draft this test?

pkg/bsql/provisioning.go (1)

221-224: Don’t drop int fields set to 0

Zero is a valid value; currently it’s excluded.

Apply this diff:

-      case "int":
-        if numValue := value.GetNumberValue(); numValue != 0 {
-          parsedValue = int(numValue)
-        }
+      case "int":
+        parsedValue = int(value.GetNumberValue())
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2835aa4 and 450e10e.

📒 Files selected for processing (6)
  • pkg/bsql/config.go (2 hunks)
  • pkg/bsql/helpers.go (0 hunks)
  • pkg/bsql/helpers_test.go (7 hunks)
  • pkg/bsql/provisioning.go (4 hunks)
  • pkg/bsql/user_syncer.go (4 hunks)
  • pkg/bsql/user_syncer_test.go (6 hunks)
💤 Files with no reviewable changes (1)
  • pkg/bsql/helpers.go
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/bsql/provisioning.go (1)
pkg/bsql/config.go (1)
  • AccountProvisioning (324-333)
pkg/bsql/user_syncer_test.go (1)
pkg/bsql/config.go (1)
  • AccountProvisioningField (336-342)
pkg/bsql/user_syncer.go (1)
pkg/bsql/resource_types.go (1)
  • ErrNoAccountProvisioningDefined (13-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: test
  • GitHub Check: go-test (ubuntu-latest)
🔇 Additional comments (4)
pkg/bsql/user_syncer_test.go (1)

12-12: Good: context propagation into tests

Using t.Context() is consistent with the new ctx-aware helpers.

pkg/bsql/helpers_test.go (1)

180-188: NoPassword case covered: looks good

The test ensures generatePassword returns empty with no error for NoPassword.

pkg/bsql/provisioning.go (2)

272-292: generatePassword: behavior is fine

Switching on LocalCredentialOptions types and delegating to crypto.GeneratePassword is correct.


8-11: Update baton-sdk dependency to v0.4.5
Pin github.com/conductorone/baton-sdk to v0.4.5 in go.mod so that LocalCredentialOptions and crypto.GeneratePassword are available.

Comment thread pkg/bsql/config.go
Comment on lines +346 to 349
NoPassword *NoPasswordConfig `yaml:"no_password,omitempty" json:"no_password,omitempty"`
RandomPassword *RandomPasswordConfig `yaml:"random_password,omitempty" json:"random_password,omitempty"`
EncryptedPassword *EncryptedPasswordConfig `yaml:"encrypted_password,omitempty" json:"encrypted_password,omitempty"`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align credential config with SDK: use PlaintextPassword instead of EncryptedPassword

LocalCredentialOptions supports PlaintextPassword; this config introduces EncryptedPassword which is unused elsewhere and mismatched. Rename to PlaintextPassword to avoid confusion and wire-up with capability details.

Apply this diff:

 type AccountCredentials struct {
   NoPassword        *NoPasswordConfig        `yaml:"no_password,omitempty" json:"no_password,omitempty"`
   RandomPassword    *RandomPasswordConfig    `yaml:"random_password,omitempty" json:"random_password,omitempty"`
-  EncryptedPassword *EncryptedPasswordConfig `yaml:"encrypted_password,omitempty" json:"encrypted_password,omitempty"`
+  PlaintextPassword *PlaintextPasswordConfig `yaml:"plaintext_password,omitempty" json:"plaintext_password,omitempty"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NoPassword *NoPasswordConfig `yaml:"no_password,omitempty" json:"no_password,omitempty"`
RandomPassword *RandomPasswordConfig `yaml:"random_password,omitempty" json:"random_password,omitempty"`
EncryptedPassword *EncryptedPasswordConfig `yaml:"encrypted_password,omitempty" json:"encrypted_password,omitempty"`
}
type AccountCredentials struct {
NoPassword *NoPasswordConfig `yaml:"no_password,omitempty" json:"no_password,omitempty"`
RandomPassword *RandomPasswordConfig `yaml:"random_password,omitempty" json:"random_password,omitempty"`
PlaintextPassword *PlaintextPasswordConfig `yaml:"plaintext_password,omitempty" json:"plaintext_password,omitempty"`
}
🤖 Prompt for AI Agents
In pkg/bsql/config.go around lines 346 to 349, the credential struct field is
named EncryptedPassword and uses EncryptedPasswordConfig which mismatches the
SDK that expects PlaintextPassword; rename the field to PlaintextPassword,
change its type to *PlaintextPasswordConfig, and update the yaml/json tags from
"encrypted_password" to "plaintext_password"; then search and update all
references/usages (constructors, unmarshalling, tests, and capability wiring) to
use PlaintextPassword and PlaintextPasswordConfig so the config aligns with the
SDK.

Comment thread pkg/bsql/config.go
Comment on lines +369 to +373
// EncryptedPasswordConfig defines configuration for encrypted password generation.
type EncryptedPasswordConfig struct {
BaseCredentialConfig `yaml:",inline"`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Rename config type to match Plaintext option

Update the config type to reflect PlaintextPassword and consistent docs.

Apply this diff:

-// EncryptedPasswordConfig defines configuration for encrypted password generation.
-type EncryptedPasswordConfig struct {
+// PlaintextPasswordConfig defines configuration for user-provided plaintext passwords.
+type PlaintextPasswordConfig struct {
   BaseCredentialConfig `yaml:",inline"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// EncryptedPasswordConfig defines configuration for encrypted password generation.
type EncryptedPasswordConfig struct {
BaseCredentialConfig `yaml:",inline"`
}
// PlaintextPasswordConfig defines configuration for user-provided plaintext passwords.
type PlaintextPasswordConfig struct {
BaseCredentialConfig `yaml:",inline"`
}
🤖 Prompt for AI Agents
In pkg/bsql/config.go around lines 369 to 373, the config type is currently
named EncryptedPasswordConfig but should reflect the Plaintext option; rename
the type and its doc comment to PlaintextPasswordConfig, keep the embedded
BaseCredentialConfig with the same yaml inline tag, and update any
references/usages, tests, and documentation to use PlaintextPasswordConfig to
maintain consistency.

Comment thread pkg/bsql/helpers_test.go
Comment thread pkg/bsql/provisioning.go
Comment thread pkg/bsql/user_syncer.go
Comment thread pkg/bsql/user_syncer.go
Comment on lines +185 to +239
func (s *userSyncer) RotateCapabilityDetails(ctx context.Context) (*v2.CredentialDetailsCredentialRotation, annotations.Annotations, error) {
l := ctxzap.Extract(ctx)
resourceTypeID, accountProvisioning, err := s.fullConfig.ExtractAccountProvisioning()
if err != nil {
if errors.Is(err, ErrNoAccountProvisioningDefined) {
return nil, nil, nil
}

return nil, nil, err
}

l.Debug("account provisioning is enabled", zap.String("resource_type_id", resourceTypeID))

if accountProvisioning == nil {
return nil, nil, errors.New("no account provisioning defined")
}

if accountProvisioning.Credentials == nil {
return nil, nil, errors.New("no credential options defined")
}

var supportedCredentials []v2.CapabilityDetailCredentialOption
var preferredCredentialOption []v2.CapabilityDetailCredentialOption

if accountProvisioning.Credentials.NoPassword != nil {
supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD)
if accountProvisioning.Credentials.NoPassword.Preferred {
preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD)
}
}

if accountProvisioning.Credentials.RandomPassword != nil {
supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_RANDOM_PASSWORD)
if accountProvisioning.Credentials.RandomPassword.Preferred {
preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_RANDOM_PASSWORD)
}
}

if len(supportedCredentials) == 0 {
return nil, nil, nil
}

if len(preferredCredentialOption) > 1 {
return nil, nil, errors.New("multiple preferred credential options are not supported")
}

if len(preferredCredentialOption) == 0 {
preferredCredentialOption = []v2.CapabilityDetailCredentialOption{supportedCredentials[0]}
}

return &v2.CredentialDetailsCredentialRotation{
SupportedCredentialOptions: supportedCredentials,
PreferredCredentialOption: preferredCredentialOption[0],
}, nil, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Rotation capability details also omit PlaintextPassword

Mirror CreateAccount capability details for rotation.

Apply this diff:

   if accountProvisioning.Credentials.RandomPassword != nil {
     supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_RANDOM_PASSWORD)
     if accountProvisioning.Credentials.RandomPassword.Preferred {
       preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_RANDOM_PASSWORD)
     }
   }
+  if accountProvisioning.Credentials.PlaintextPassword != nil {
+    supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD)
+    if accountProvisioning.Credentials.PlaintextPassword.Preferred {
+      preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD)
+    }
+  }
🤖 Prompt for AI Agents
In pkg/bsql/user_syncer.go around lines 185 to 239, Rotation capability details
currently only include NoPassword and RandomPassword and therefore omit
PlaintextPassword; update the function to mirror CreateAccount capability
behavior by adding handling for
accountProvisioning.Credentials.PlaintextPassword: append the corresponding
v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD
to supportedCredentials when non-nil, and if PlaintextPassword.Preferred is true
append it to preferredCredentialOption; ensure preferred selection logic (max
one preferred, default to first supported) remains unchanged and return the
preferredCredentialOption[0] as before.

cursor[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 450e10e and 50ae13b.

⛔ Files ignored due to path filters (8)
  • .github/workflows/ci.yaml is excluded by none and included by none
  • .gon-amd64.json is excluded by none and included by none
  • .gon-arm64.json is excluded by none and included by none
  • .goreleaser.docker.yaml is excluded by none and included by none
  • .goreleaser.yaml is excluded by none and included by none
  • Dockerfile.test is excluded by none and included by none
  • docker-compose-postgres-test.yml is excluded by none and included by none
  • examples/postgres-test.yml is excluded by none and included by none
📒 Files selected for processing (4)
  • pkg/bsql/config.go (5 hunks)
  • pkg/bsql/provisioning.go (5 hunks)
  • pkg/bsql/resource_types.go (1 hunks)
  • pkg/bsql/user_syncer.go (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
pkg/bsql/resource_types.go (1)
pkg/connector/connector.go (1)
  • New (83-90)
pkg/bsql/user_syncer.go (2)
pkg/bsql/config.go (1)
  • AccountCredentials (348-352)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
pkg/bsql/config.go (1)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
pkg/bsql/provisioning.go (3)
pkg/bsql/config.go (2)
  • AccountProvisioning (327-336)
  • CredentialRotation (396-403)
pkg/bsql/sql_syncer.go (1)
  • SQLSyncer (20-27)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (4)
pkg/bsql/config.go (1)

349-376: Rename EncryptedPassword option to PlaintextPassword

LocalCredentialOptions only exposes NoPassword, RandomPassword, and PlaintextPassword branches. Introducing EncryptedPassword here means we surface a credential option the SDK neither understands nor can marshal, and the downstream code already fails to generate or advertise such an option. Please rename this field/type (and the associated YAML/JSON tags) back to PlaintextPassword so the config stays aligned with the SDK schema.

Apply this diff (and update call sites accordingly):

 type AccountCredentials struct {
 	NoPassword        *NoPasswordConfig        `yaml:"no_password,omitempty" json:"no_password,omitempty"`
 	RandomPassword    *RandomPasswordConfig    `yaml:"random_password,omitempty" json:"random_password,omitempty"`
-	EncryptedPassword *EncryptedPasswordConfig `yaml:"encrypted_password,omitempty" json:"encrypted_password,omitempty"`
+	PlaintextPassword *PlaintextPasswordConfig `yaml:"plaintext_password,omitempty" json:"plaintext_password,omitempty"`
 }
 
-// EncryptedPasswordConfig defines configuration for encrypted password generation.
-type EncryptedPasswordConfig struct {
+// PlaintextPasswordConfig defines configuration for user-provided plaintext passwords.
+type PlaintextPasswordConfig struct {
 	BaseCredentialConfig `yaml:",inline"`
 }
pkg/bsql/user_syncer.go (2)

40-78: Advertise Plaintext credential support instead of Encrypted

Same issue as the config: the SDK’s enum exposes CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD, not an encrypted variant. The current branch will fail to compile/advertise the wrong capability. Please switch this block back to Plaintext so capability details line up with the actual options we can issue.

-	if credentials.EncryptedPassword != nil {
-		supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_ENCRYPTED_PASSWORD)
-		if credentials.EncryptedPassword.Preferred {
-			preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_ENCRYPTED_PASSWORD)
-		}
-	}
+	if credentials.PlaintextPassword != nil {
+		supportedCredentials = append(supportedCredentials, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD)
+		if credentials.PlaintextPassword.Preferred {
+			preferredCredentialOption = append(preferredCredentialOption, v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_PLAINTEXT_PASSWORD)
+		}
+	}

169-193: Rotation queries need resource and credential context

We currently execute the update queries with only password in the parameter map, leaving resourceId completely unused and never attaching the credentials namespace. That prevents the SQL from targeting the specific user and from referencing credentials.password, and on top of that we enqueue an empty password when LocalCredentialOptions is NoPassword. Please populate the resource identifiers, only add password values when non-empty, and wire the credentials namespace before running the queries.

-	queryInputs := make(map[string]any)
-	credentials := make(map[string]any)
+	queryInputs := make(map[string]any)
+	credentials := make(map[string]any)
+	if resourceId == nil || resourceId.GetResource() == "" {
+		return nil, nil, errors.New("resource id is required for rotation")
+	}
+	queryInputs["resource"] = resourceId.GetResource()
+	if rt := resourceId.GetResourceType(); rt != "" {
+		queryInputs["resource_type"] = rt
+	}
 
 	var plaintextDataList []*v2.PlaintextData
 	password, err := generatePassword(ctx, credentialOptions)
 	if err != nil {
 		return nil, nil, err
 	}
-	queryInputs["password"] = password
-	credentials["password"] = password
-	// Create plaintext data for return
-	passwordData := &v2.PlaintextData{
-		Name:  "password",
-		Bytes: []byte(password),
-	}
-	plaintextDataList = append(plaintextDataList, passwordData)
+	if password != "" {
+		queryInputs["password"] = password
+		credentials["password"] = password
+		passwordData := &v2.PlaintextData{
+			Name:  "password",
+			Bytes: []byte(password),
+		}
+		plaintextDataList = append(plaintextDataList, passwordData)
+	}
+	if len(credentials) > 0 {
+		queryInputs["credentials"] = credentials
+	}
pkg/bsql/provisioning.go (1)

241-254: Skip injecting blank password for NoPassword option

generatePassword returns "" for LocalCredentialOptions_NoPassword, yet we still set queryInputs["password"] and return empty plaintext data. That regresses the prior behavior where the password fields were omitted entirely. Guard on password != "" before touching the maps or plaintext list.

-		queryInputs["password"] = password
-		credentials["password"] = password
-		// Create plaintext data for return
-		passwordData := &v2.PlaintextData{
-			Name:  "password",
-			Bytes: []byte(password),
-		}
-		plaintextDataList = append(plaintextDataList, passwordData)
+		if password != "" {
+			queryInputs["password"] = password
+			credentials["password"] = password
+			passwordData := &v2.PlaintextData{
+				Name:  "password",
+				Bytes: []byte(password),
+			}
+			plaintextDataList = append(plaintextDataList, passwordData)
+		}

Comment thread pkg/bsql/user_syncer.go Outdated
Comment on lines +162 to +176
resourceTypeID, rotationConfig, err := s.extractAndValidateCredentialRotation()
if err != nil {
return nil, nil, err
}

logger.Debug("rotating credential", zap.String("resource_type_id", resourceTypeID))

queryInputs := make(map[string]any)
credentials := make(map[string]any)
var plaintextDataList []*v2.PlaintextData
password, err := generatePassword(ctx, credentialOptions)
if err != nil {
return nil, nil, err
}
queryInputs["password"] = password

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid nil dereference when rotation config is absent

extractAndValidateCredentialRotation returns ( "", nil, nil ) when no rotation block is defined. Line [168] immediately dereferences rotationConfig, which will panic in that case. Short-circuit by returning ErrNoCredentialRotationDefined (or similar) before accessing the struct.

	resourceTypeID, rotationConfig, err := s.extractAndValidateCredentialRotation()
	if err != nil {
		return nil, nil, err
	}
+	if rotationConfig == nil {
+		return nil, nil, ErrNoCredentialRotationDefined
+	}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In pkg/bsql/user_syncer.go around lines 162 to 176,
extractAndValidateCredentialRotation can return ("", nil, nil) when no rotation
block is defined, but the code immediately dereferences rotationConfig causing a
possible panic; fix by checking if rotationConfig == nil right after the call
and return a sentinel error such as ErrNoCredentialRotationDefined (or create it
if missing) before any access to rotationConfig, so the function short-circuits
safely instead of dereferencing a nil pointer.

@ggreer ggreer changed the title (WIP) Add support for setting passwords Add support for setting passwords Sep 26, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4388836 and dcfff92.

📒 Files selected for processing (1)
  • pkg/bsql/helpers_test.go (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot

Comment thread pkg/bsql/helpers_test.go
…word into inputs, and don't return encrypted password data.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcfff92 and f9b966d.

📒 Files selected for processing (3)
  • pkg/bsql/helpers_test.go (8 hunks)
  • pkg/bsql/provisioning.go (5 hunks)
  • pkg/bsql/user_syncer.go (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/bsql/user_syncer.go (2)
pkg/bsql/config.go (1)
  • AccountCredentials (348-352)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
pkg/bsql/provisioning.go (3)
pkg/bsql/config.go (2)
  • AccountProvisioning (327-336)
  • CredentialRotation (396-401)
pkg/bsql/sql_syncer.go (1)
  • SQLSyncer (20-27)
pkg/bsql/resource_types.go (1)
  • ErrNoCredentialRotationDefined (14-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: go-test (ubuntu-latest)
🔇 Additional comments (2)
pkg/bsql/user_syncer.go (1)

163-177: Guard against missing rotation config

extractAndValidateCredentialRotation can return ( "", nil, nil ) when the config is absent, and we dereference it immediately. That panics. Short-circuit with the sentinel error before touching the struct.

 	resourceTypeID, rotationConfig, err := s.extractAndValidateCredentialRotation()
 	if err != nil {
 		return nil, nil, err
 	}
+	if rotationConfig == nil {
+		return nil, nil, ErrNoCredentialRotationDefined
+	}
pkg/bsql/helpers_test.go (1)

190-200: Set the expected plaintext password

The new assertion never fires because expectedValue stays empty for the plaintext case, so we still don’t verify pass-through behavior. Populate it so the comparison actually runs.

 		{
 			name: "valid plaintext password",
 			credentialOptions: &v2.LocalCredentialOptions{
 				Options: &v2.LocalCredentialOptions_PlaintextPassword_{
 					PlaintextPassword: &v2.LocalCredentialOptions_PlaintextPassword{
 						PlaintextPassword: "password",
 					},
 				},
 			},
 			expectError:    false,
 			expectNonEmpty: true,
+			expectedValue:  "password",
 		},

Comment thread pkg/bsql/provisioning.go
Comment thread pkg/bsql/provisioning.go
Comment on lines +284 to +292
case *v2.LocalCredentialOptions_RandomPassword_, *v2.LocalCredentialOptions_PlaintextPassword_:
password, err = crypto.GeneratePassword(ctx, credentialOptions)
if err != nil {
return nil, fmt.Errorf("failed to generate password: %w", err)
}

default:
return nil, fmt.Errorf("unsupported credential options: %v", credentialOptions)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Handle encrypted password options

generatePassword now rejects LocalCredentialOptions_EncryptedPassword_, even though the capability advertises support for it. That breaks encrypted-password provisioning/rotation. Include the encrypted case so we still delegate to crypto.GeneratePassword.

-	case *v2.LocalCredentialOptions_RandomPassword_, *v2.LocalCredentialOptions_PlaintextPassword_:
+	case *v2.LocalCredentialOptions_RandomPassword_,
+		*v2.LocalCredentialOptions_PlaintextPassword_,
+		*v2.LocalCredentialOptions_EncryptedPassword_:
🤖 Prompt for AI Agents
In pkg/bsql/provisioning.go around lines 284 to 292, the switch over
LocalCredentialOptions omits the EncryptedPassword case so encrypted password
types are rejected; add the *v2.LocalCredentialOptions_EncryptedPassword_ type
to the case list (or add a separate case that delegates to
crypto.GeneratePassword) so encrypted-password options are passed through to
crypto.GeneratePassword and handled like RandomPassword/PlaintextPassword,
leaving the default branch unchanged.

Comment thread pkg/bsql/user_syncer.go Outdated
Comment on lines +178 to +196
queryInputs["password"] = password
credentials["password"] = password
// Create plaintext data for return
passwordData := &v2.PlaintextData{
Name: "password",
Bytes: []byte(*password),
}
plaintextDataList = append(plaintextDataList, passwordData)
}

// Execute account creation queries
useTransaction := !rotationConfig.Update.NoTransaction
if err := s.runProvisioningQueries(ctx, rotationConfig.Update.Queries, queryInputs, useTransaction); err != nil {
return nil, nil, err
}

return plaintextDataList, nil, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Return strings (and expose credentials) during rotation

Same pointer issue as provisioning: storing *string in queryInputs breaks Exec. Additionally, we build a credentials map but never expose it, so CEL expressions like credentials.password fail. Mirror the create flow by storing the string and namespacing the credentials.

 	var plaintextDataList []*v2.PlaintextData
 	password, err := generatePassword(ctx, credentialOptions)
 	if err != nil {
 		return nil, nil, err
 	}
 	if password != nil {
-		queryInputs["password"] = password
-		credentials["password"] = password
+		pw := *password
+		queryInputs["password"] = pw
+		credentials["password"] = pw
 		// Create plaintext data for return
 		passwordData := &v2.PlaintextData{
 			Name:  "password",
-			Bytes: []byte(*password),
+			Bytes: []byte(pw),
 		}
 		plaintextDataList = append(plaintextDataList, passwordData)
 	}
+	if len(credentials) > 0 {
+		queryInputs["credentials"] = credentials
+	}

@ggreer
ggreer merged commit e2da9e3 into main Sep 27, 2025
5 checks passed
@ggreer
ggreer deleted the ggreer/password branch September 27, 2025 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants