-
Notifications
You must be signed in to change notification settings - Fork 1
Add spec for tiger db create role command
#58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM to me overall 👍.
Left a couple comments/questions below, but nothing major.
One other thought: I know CREATE ROLE accepts a bunch of options (see the docs). Do we need to support configuring any of those via the CLI? Even if not, we should probably document the exact options that are set when creating roles via this command.
| **Combining with `--from` for Safe Table Access:** | ||
| The `--from` flag allows you to create a read-only role that inherits the same table access as your application role: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the default behavior if --from is not specified? What permissions does the new role have in that case? We should probably document the exact behavior (it's not immediately clear to me, as someone who hasn't worked a ton with Postgres roles)
09b10d2 to
46d46b9
Compare
tiger db create role commandtiger db create role command
Adds comprehensive specification for creating database roles with read-only enforcement for safe AI agent access. Key features: - Role creation with optional service ID (uses default if omitted) - `--read-only` flag for permanent read-only enforcement via tsdb_admin extension - `--from` flag to inherit grants from comma-separated list of roles - `--statement-timeout` for performance safety - Auto-generated passwords with manual override options - `--name` flag required to prevent accidental role creation Includes detailed documentation on: - Read-only mode implementation and use cases - Password behavior (auto-generate, explicit value, env var, prompt) - Design rationale for requiring explicit --name flag - AI agent safety patterns combining read-only + timeouts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
This commit implements the core functionality of the `tiger db create role` command as specified in specs/spec.md. The command creates PostgreSQL roles with optional read-only enforcement, statement timeouts, and role inheritance. ## What Works ✅ Basic role creation with auto-generated passwords ✅ Role creation with explicit passwords (--password flag or TIGER_NEW_PASSWORD env var) ✅ Read-only enforcement via --read-only flag (tsdb_admin.read_only_role GUC) ✅ Statement timeout configuration via --statement-timeout flag ✅ Password storage in configured storage (keyring/pgpass/none) ✅ Output formatting (JSON/YAML/table) via --output flag ✅ SQL injection prevention for role names using pgx.Identifier.Sanitize() ✅ Password security: fails if password contains single quotes (no escaping needed) ✅ Comprehensive unit tests for SQL generation functions ✅ Integration tests for basic role creation scenarios ## Known Issue: --from Flag with tsdbadmin ❌ The --from flag FAILS when trying to inherit from tsdbadmin role ❌ Error: "permission denied to grant role \"tsdbadmin\" (SQLSTATE 42501)" ### Root Cause PostgreSQL's CREATE ROLE ... IN ROLE syntax is equivalent to GRANT: ```sql CREATE ROLE new_role IN ROLE tsdbadmin -- equivalent to: GRANT tsdbadmin TO new_role ``` To grant a role, you need either: 1. Be a superuser (tsdbadmin is NOT a superuser in TimescaleDB Cloud) 2. Have the role WITH ADMIN OPTION (tsdbadmin does NOT have ADMIN OPTION on itself) Since tsdbadmin doesn't have ADMIN OPTION on itself, it cannot grant itself to other roles, even though it can create new roles. ### Impact This is a CRITICAL limitation because the most common use case is: ```bash tiger db create role --name ai_analyst --from tsdbadmin --read-only ``` Users expect this to create a role with the same permissions as tsdbadmin, which is essential for AI agents, analytics users, etc. ### Solution Options (TODO) 1. **Copy grants explicitly** (recommended): When --from is specified, query all grants that the source role has and apply them directly to the new role instead of using IN ROLE. This bypasses the ADMIN OPTION requirement. 2. **Try IN ROLE, fallback to copying**: Try the simple IN ROLE approach first, if it fails with permission denied, automatically fall back to copying grants. 3. **Request platform change**: Ask TimescaleDB Cloud to grant tsdbadmin the ADMIN OPTION on itself so it can grant itself to other roles. 4. **Document limitation**: Update docs to explain that --from only works with roles you've created yourself, not with tsdbadmin. ### Integration Test Status Most integration tests PASS: - ✅ CreateRole_Basic - ✅ CreateRole_WithExplicitPassword - ❌ CreateRole_WithInheritedGrants (fails due to --from tsdbadmin) - ✅ CreateRole_ReadOnly - ✅ CreateRole_WithStatementTimeout - ❌ CreateRole_AllOptions (fails due to --from tsdbadmin) - ✅ CreateRole_DuplicateName Tests that use --from tsdbadmin fail but are left in place to demonstrate the issue that needs to be fixed. ## Technical Details ### Password Handling - Passwords are quoted as PostgreSQL string literals: 'password' - Single quotes in passwords cause an error (fail-fast approach) - PostgreSQL's CREATE ROLE doesn't support parameterized queries for passwords - Auto-generated passwords use base64 URL-safe encoding (no special chars) ### SQL Generation - Role names: pgx.Identifier.Sanitize() for proper quoting and escaping - Passwords: Manual quoting with single quote validation - Multiple roles in --from: Comma-separated quoted identifiers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
…g for --from=tsdbadmin tests ## New Test: CreateRole_ReadOnlyWithInheritance This test verifies that the --from flag works correctly when inheriting from a role we control (not tsdbadmin), and that read-only enforcement actually works: 1. Creates a base role (tsdbadmin automatically gets ADMIN OPTION on it) 2. Grants CREATE privilege on public schema to base role 3. Base role creates a table and inserts test data 4. Creates a read-only role with --from base_role --read-only 5. Verifies the read-only role CAN read the data 6. Verifies the read-only role CANNOT write (enforced by tsdb_admin.read_only_role) 7. Cleans up the test table This demonstrates that: - The --from flag works when we have ADMIN OPTION on the source role - Read-only enforcement prevents writes while allowing reads - Role inheritance allows the new role to access tables created by the base role ## Skip Flag for --from=tsdbadmin Tests Added `skipFromTsdbadminTests` constant (default: true) to control whether to skip tests that use --from=tsdbadmin. When true, these tests are skipped with a warning explaining the limitation:⚠️ Skipping --from=tsdbadmin test: tsdbadmin doesn't have ADMIN OPTION on itself, so it can't grant itself to other roles. This is a known limitation that needs to be fixed. Affected tests: - CreateRole_WithInheritedGrants - CreateRole_AllOptions Set `skipFromTsdbadminTests = false` to see the actual permission failures when debugging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
## Problem Solved The --from tsdbadmin flag was failing with "permission denied to grant role" because tsdbadmin doesn't have ADMIN OPTION on itself, preventing it from granting itself to other roles via standard PostgreSQL IN ROLE syntax. ## Solution Use TimescaleDB Cloud special functions when --from includes tsdbadmin: - timescale_functions.create_bare_readonly_role($1, $2) to create the role - timescale_functions.grant_tsdbadmin_to_role($1) to grant privileges ## Implementation Details ### Requirements When Using --from tsdbadmin 1. **Must use --read-only flag**: The create_bare_readonly_role function only creates read-only roles, so --read-only is required 2. **Cannot use --statement-timeout**: Permission denied to ALTER roles created with special functions, so this combination is now disallowed ### Code Changes - Updated createRoleWithOptions() to detect tsdbadmin in --from list - Use special functions path when tsdbadmin is present - Enforce --read-only requirement with clear error message - Disallow --statement-timeout with clear error message - Use parameterized queries ($1, $2) for function calls ### Test Updates - Removed skipFromTsdbadminTests flag (no longer needed) - Updated CreateRole_WithInheritedGrants to use --read-only - Updated CreateRole_AllOptions to remove --statement-timeout - Added CreateRole_ReadOnlyWithInheritance test for non-tsdbadmin inheritance - All CreateRole integration tests now pass ### Added Integration Test Script Created scripts/test-integration.sh for easier test execution: - Loads environment from .env automatically - Builds binary before testing - Supports pattern matching for specific tests - Provides colored output for better readability - Documented in CLAUDE.md ## Test Results ✅ All ServiceLifecycleIntegration tests passing (66s) ✅ CreateRole with --from tsdbadmin now works correctly ✅ Read-only enforcement verified ✅ Role inheritance verified 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
…oject-id flag ## Problem TestAuthenticationErrorsIntegration was failing because it was using --project-id flag on service and db commands, but this flag doesn't exist. The test was getting "unknown flag: --project-id" errors instead of authentication errors. ## Root Cause The --project-id flag doesn't exist as a persistent flag in the CLI. It's only valid for the auth login command where it specifies which project to authenticate against. The available persistent flags are: - --config-dir - --debug - --service-id - --analytics - --password-storage - --skip-update-check - --color ## Fix Removed --project-id flag from all service and db command test cases in TestAuthenticationErrorsIntegration. The flag remains in auth login commands where it's valid. ## Test Results ✅ All integration tests now pass (67.4s) - TestServiceLifecycleIntegration: PASS - TestAuthenticationErrorsIntegration: PASS - TestInstallMCPForEditor_Integration: PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
46d46b9 to
bc50420
Compare
tiger db create role command tiger db create role command
| { | ||
| name: "db connect", | ||
| args: []string{"db", "connect", "non-existent-service", "--project-id", projectID}, | ||
| args: []string{"db", "connect", "non-existent-service"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixes like this are for things that were broken from before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a handful of comments, but LGTM overall.
Didn't test yet, but I can do that in a bit if you'd like.
Based on reviewer feedback from nathanjcochran: Code improvements: - Fix duplicative GRANT logic: moved otherRoles grants inside hasTsdbadmin block since IN ROLE clause handles grants in the standard path - Switch --from flag to StringSliceVar for consistency with --addons flag pattern (supports both --from role1,role2 and --from role1 --from role2) - Remove unnecessary cleanupRoles() function - service deletion handles cleanup - Rename TestServiceNotFound to TestServiceNotFoundIntegration to match pattern - Make error message checks in tests more flexible for API variations Documentation updates: - Change TIGER_NEW_PASSWORD example to inline format (not export) - Replace "PostgreSQL GUCs" with clearer "PostgreSQL Configuration Parameters" - Document default behavior when --from is not specified (LOGIN/PASSWORD only) - Update CLAUDE.md for simplified test script usage Test script simplification: - Reduce test-integration.sh from 84 to 31 lines following Unix philosophy - Use simple env + go test approach with $@ for argument passthrough - Keep essential .env checks and build step 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Summary
tiger db create rolecommandKey Features Documented
--nameflagtsdb_admin.read_only_role--fromflag accepts comma-separated list of roles--statement-timeoutfor query limitsTIGER_NEW_PASSWORDenv var, or interactive promptDesign Decisions
--nameflag is required (not positional) to prevent accidental role creation with service IDsDocumentation Includes
🤖 Generated with Claude Code