Skip to content

Update user in tables to use hashed password#1667

Merged
MarceloRGonc merged 89 commits intomainfrom
mg/OPS-3123
Nov 26, 2025
Merged

Update user in tables to use hashed password#1667
MarceloRGonc merged 89 commits intomainfrom
mg/OPS-3123

Conversation

@MarceloRGonc
Copy link
Copy Markdown
Contributor

@MarceloRGonc MarceloRGonc commented Nov 25, 2025

Fixes OPS-3123.

Summary by CodeRabbit

  • Chores

    • Updated OpenOps Tables service to version 0.2.11 across deployment configurations (docker-compose, Helm, and environment)
  • Refactor

    • Enhanced password authentication and user management workflows
    • Improved password handling during user creation and admin operations
    • Updated password reset processes
  • Tests

    • Updated authentication unit tests to reflect password handling changes

✏️ Tip: You can customize this high-level summary in your review settings.

@MarceloRGonc MarceloRGonc changed the base branch from main to mg/cleanup November 26, 2025 10:00
Base automatically changed from mg/cleanup to main November 26, 2025 10:05
@MarceloRGonc MarceloRGonc marked this pull request as ready for review November 26, 2025 10:59
Copilot AI review requested due to automatic review settings November 26, 2025 10:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the authentication flow to use hashed passwords from the database when authenticating with OpenOps Tables, rather than plain-text passwords from environment variables. This involves retrieving the user from the database to access their hashed password, updating the password management workflow, and including a migration to synchronize existing user passwords with the Tables system.

Key changes:

  • Modified authentication to retrieve and use hashed passwords from the database instead of plain-text environment variables
  • Added a database migration to sync existing user passwords with OpenOps Tables
  • Updated the updatePassword function to return the updated user object

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/server/api/src/app/openops-tables/auth-admin-tables.ts Modified to retrieve admin user from database and use hashed password for authentication
packages/server/api/src/app/user/user-service.ts Changed updatePassword to return the updated User object
packages/server/api/src/app/database/seeds/seed-admin.ts Updated admin seeding to sync hashed passwords with Tables system
packages/server/api/src/app/authentication/basic/authentication-service.ts Changed to use hashed password from user object instead of request password
packages/server/api/src/app/authentication/new-user/create-user.ts Updated to pass hashed password from user object to Tables
packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts New migration to sync existing user passwords with OpenOps Tables
packages/server/api/src/app/database/postgres-connection.ts Registered the new migration
packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts Updated test to mock user service and remove password environment variable checks
packages/server/api/test/unit/authentication/new-user/create-user.test.ts Updated test assertion to use hashed password from user object
packages/server/api/src/app/openops-tables/template-tables/seed-tables-for-templates.ts Removed unused import
packages/server/api/src/app/openops-tables/index.ts Removed exported authentication function (no longer needed in public API)
deploy/helm/openops/values.yaml Bumped openops-tables version to 0.2.11
deploy/docker-compose/docker-compose.yml Bumped openops-tables version to 0.2.11
compose.yaml Bumped openops-tables version to 0.2.11

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/server/api/src/app/database/seeds/seed-admin.ts
Comment thread packages/server/api/src/app/database/seeds/seed-admin.ts
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Nov 26, 2025

Greptile Overview

Greptile Summary

This PR migrates the OpenOps Tables service (Baserow) to use bcrypt-hashed passwords that match the OpenOps database, replacing the previous plaintext password flow. The Tables service version is upgraded from 0.2.10 to 0.2.11 to support this change.

  • All authentication and user creation flows updated to pass hashed passwords to Tables API
  • Migration script synchronizes existing user passwords from OpenOps DB to Tables
  • Admin seeding logic updated to sync passwords on user creation and updates
  • userService.updatePassword now returns the updated User object for password syncing
  • Tests updated to reflect new password handling behavior

Critical Issue Found:

  • The authentication flow now passes hashed passwords to authenticateUserInOpenOpsTables, which calls the Tables /api/user/token-auth/ endpoint. This breaks standard authentication where the endpoint expects plaintext passwords to hash and verify. This only works if Tables 0.2.11 was modified to accept pre-hashed passwords for authentication, which is an unusual authentication pattern.

Confidence Score: 2/5

  • This PR has a critical authentication flow change that could break user login if the Tables service wasn't updated correctly
  • The score reflects a fundamental change in authentication architecture where hashed passwords are now passed to authentication endpoints. While the Tables version is being upgraded (0.2.10 -> 0.2.11), this non-standard authentication flow (passing pre-hashed passwords instead of plaintext) could cause authentication failures if both services aren't perfectly synchronized
  • Pay close attention to packages/server/api/src/app/authentication/basic/authentication-service.ts and verify that Tables 0.2.11 actually supports authentication with pre-hashed passwords

Important Files Changed

File Analysis

Filename Score Overview
packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts 2/5 Migration to sync hashed passwords from OpenOps DB to Tables - uses raw passwords from DB which are already hashed as migration input
packages/server/api/src/app/authentication/basic/authentication-service.ts 5/5 Updated to pass hashed password to Tables authentication instead of plaintext password from request
packages/server/api/src/app/openops-tables/auth-admin-tables.ts 5/5 Updated to fetch user and use hashed password instead of plaintext from environment variable
packages/server/api/src/app/database/seeds/seed-admin.ts 5/5 Updated admin seeding to sync hashed password to Tables after user creation/password update

Sequence Diagram

sequenceDiagram
    participant User
    participant OpenOpsAPI
    participant OpenOpsDB
    participant TablesAPI
    
    Note over OpenOpsAPI,TablesAPI: User Sign In Flow
    User->>OpenOpsAPI: POST /signin (email, plaintext password)
    OpenOpsAPI->>OpenOpsDB: Fetch user by email
    OpenOpsDB-->>OpenOpsAPI: User with hashed password
    OpenOpsAPI->>OpenOpsAPI: Compare plaintext with hash (bcrypt)
    OpenOpsAPI->>TablesAPI: POST /api/user/token-auth/ (email, hashed password)
    TablesAPI-->>OpenOpsAPI: Auth tokens
    OpenOpsAPI-->>User: Success with tokens
    
    Note over OpenOpsAPI,TablesAPI: User Creation Flow
    User->>OpenOpsAPI: POST /signup (email, plaintext password)
    OpenOpsAPI->>OpenOpsAPI: Hash password (bcrypt)
    OpenOpsAPI->>OpenOpsDB: Save user with hashed password
    OpenOpsDB-->>OpenOpsAPI: User created
    OpenOpsAPI->>TablesAPI: POST /api/user/ (email, hashed password)
    TablesAPI-->>OpenOpsAPI: User created with refresh token
    OpenOpsAPI-->>User: Success
    
    Note over OpenOpsAPI,TablesAPI: Password Update Flow
    OpenOpsAPI->>OpenOpsAPI: Hash new password
    OpenOpsAPI->>OpenOpsDB: Update user password (hashed)
    OpenOpsDB-->>OpenOpsAPI: Updated
    OpenOpsAPI->>TablesAPI: PATCH /api/admin/users/ (email, hashed password)
    TablesAPI-->>OpenOpsAPI: Password updated
    
    Note over OpenOpsAPI,TablesAPI: Migration Flow
    OpenOpsAPI->>OpenOpsDB: SELECT email, password FROM user
    OpenOpsDB-->>OpenOpsAPI: All users with hashed passwords
    OpenOpsAPI->>TablesAPI: Authenticate as admin
    TablesAPI-->>OpenOpsAPI: Admin token
    loop For each non-admin user
        OpenOpsAPI->>TablesAPI: PATCH /api/admin/users/ (email, hashed password)
    end
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

14 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
packages/server/api/test/unit/authentication/new-user/create-user.test.ts (1)

50-61: Test mock is missing the password field.

The createdUser mock doesn't include a password property, so res.user.password at line 86 will be undefined. This makes the assertion less meaningful. Compare with the test at line 132-154 which correctly includes password: '12345678' in the mock.

Add a password field to the mock to properly test the behavior:

     const createdUser = {
       id: 'u1',
       email: baseParams.email,
       organizationId: baseParams.organizationId,
       firstName: baseParams.firstName,
       lastName: baseParams.lastName,
       status: UserStatus.ACTIVE,
       organizationRole: OrganizationRole.MEMBER,
       verified: baseParams.verified,
       trackEvents: baseParams.trackEvents,
       newsLetter: baseParams.newsLetter,
+      password: 'hashed-password',
     };

Also applies to: 83-88

packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts (2)

64-66: Remove unused password mock.

The second mockReturnValueOnce('secret') appears to be a leftover from the old implementation where OPENOPS_ADMIN_PASSWORD was retrieved via getOrThrow. Since the password now comes from the user object (Line 42), this second mock return value is no longer needed.

Apply this diff:

-    getOrThrowMock
-      .mockReturnValueOnce('admin@example.com')
-      .mockReturnValueOnce('secret');
+    getOrThrowMock.mockReturnValueOnce('admin@example.com');

90-92: Fix password mismatch and remove unused mock.

Two issues:

  1. The mock returns 'pwd' but the user object in beforeEach (Line 42) has password: 'secret'
  2. The second mockReturnValueOnce for the password is no longer used since passwords now come from the user object

Apply this diff:

-    getOrThrowMock
-      .mockReturnValueOnce('admin2@example.com')
-      .mockReturnValueOnce('pwd');
+    getOrThrowMock.mockReturnValueOnce('admin2@example.com');

Additionally, verify the test expectations at lines 98-103 match the mocked user's password 'secret', not 'pwd'.

🧹 Nitpick comments (1)
packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts (1)

38-40: Document the irreversible nature of this migration.

The lack of rollback implementation is understandable given that password resets in an external system (OpenOps Tables) cannot be easily reversed without storing previous state. However, this should be clearly documented in the PR description or migration comments to inform operators that this migration is irreversible.

Consider adding a comment explaining why rollback is not supported:

 public async down(_: QueryRunner): Promise<void> {
+  // Rollback is not supported because this migration updates passwords in
+  // the external OpenOps Tables system, which cannot be reversed without
+  // storing the previous password state (which we don't have).
   throw new Error('Rollback not implemented');
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba42672 and 66d03f9.

📒 Files selected for processing (14)
  • compose.yaml (1 hunks)
  • deploy/docker-compose/docker-compose.yml (2 hunks)
  • deploy/helm/openops/values.yaml (1 hunks)
  • packages/server/api/src/app/authentication/basic/authentication-service.ts (1 hunks)
  • packages/server/api/src/app/authentication/new-user/create-user.ts (1 hunks)
  • packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts (1 hunks)
  • packages/server/api/src/app/database/postgres-connection.ts (2 hunks)
  • packages/server/api/src/app/database/seeds/seed-admin.ts (4 hunks)
  • packages/server/api/src/app/openops-tables/auth-admin-tables.ts (2 hunks)
  • packages/server/api/src/app/openops-tables/index.ts (0 hunks)
  • packages/server/api/src/app/openops-tables/template-tables/seed-tables-for-templates.ts (0 hunks)
  • packages/server/api/src/app/user/user-service.ts (2 hunks)
  • packages/server/api/test/unit/authentication/new-user/create-user.test.ts (1 hunks)
  • packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts (3 hunks)
💤 Files with no reviewable changes (2)
  • packages/server/api/src/app/openops-tables/template-tables/seed-tables-for-templates.ts
  • packages/server/api/src/app/openops-tables/index.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Indentation: 2 spaces for TypeScript/JavaScript
Braces required for all control blocks, even single-line
One space between keywords and parentheses: if (condition) {
Use camelCase for variables and functions
Use PascalCase for classes and types
Use UPPER_SNAKE_CASE for constants
Use lowercase with hyphens for file names (e.g., user-profile.ts)
Prefer const over let or var in TypeScript/JavaScript
Prefer arrow functions for callbacks and functional components in TypeScript/JavaScript

Files:

  • packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts
  • packages/server/api/src/app/openops-tables/auth-admin-tables.ts
  • packages/server/api/test/unit/authentication/new-user/create-user.test.ts
  • packages/server/api/src/app/user/user-service.ts
  • packages/server/api/src/app/authentication/new-user/create-user.ts
  • packages/server/api/src/app/database/postgres-connection.ts
  • packages/server/api/src/app/authentication/basic/authentication-service.ts
  • packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts
  • packages/server/api/src/app/database/seeds/seed-admin.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx}: Use types and interfaces where appropriate in TypeScript
Use explicit return types for exported functions in TypeScript

Files:

  • packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts
  • packages/server/api/src/app/openops-tables/auth-admin-tables.ts
  • packages/server/api/test/unit/authentication/new-user/create-user.test.ts
  • packages/server/api/src/app/user/user-service.ts
  • packages/server/api/src/app/authentication/new-user/create-user.ts
  • packages/server/api/src/app/database/postgres-connection.ts
  • packages/server/api/src/app/authentication/basic/authentication-service.ts
  • packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts
  • packages/server/api/src/app/database/seeds/seed-admin.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{tsx,ts}: Frontend tech stack must strictly use: React 18, Zustand, react-query v5, shadcn, and Axios with qs package for query strings
Follow best practices for React hooks
Prefer small, composable components and extract helper functions where possible
Use cn utility to group tailwind classnames in React components

Files:

  • packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts
  • packages/server/api/src/app/openops-tables/auth-admin-tables.ts
  • packages/server/api/test/unit/authentication/new-user/create-user.test.ts
  • packages/server/api/src/app/user/user-service.ts
  • packages/server/api/src/app/authentication/new-user/create-user.ts
  • packages/server/api/src/app/database/postgres-connection.ts
  • packages/server/api/src/app/authentication/basic/authentication-service.ts
  • packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts
  • packages/server/api/src/app/database/seeds/seed-admin.ts
🧬 Code graph analysis (5)
packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts (3)
packages/server/shared/src/lib/system/system.ts (1)
  • system (104-200)
packages/openops/src/lib/openops-tables/auth-user.ts (1)
  • authenticateUserInOpenOpsTables (10-30)
packages/openops/src/lib/openops-tables/reset-user-password.ts (1)
  • resetUserPassword (4-17)
packages/server/api/src/app/openops-tables/auth-admin-tables.ts (1)
packages/openops/src/lib/openops-tables/auth-user.ts (1)
  • authenticateUserInOpenOpsTables (10-30)
packages/server/api/src/app/user/user-service.ts (1)
packages/shared/src/lib/user/user.ts (2)
  • User (28-41)
  • User (43-43)
packages/server/api/src/app/database/postgres-connection.ts (1)
packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts (1)
  • MigrateTablesUserPassword1763755045436 (8-41)
packages/server/api/src/app/database/seeds/seed-admin.ts (3)
packages/openops/src/lib/openops-tables/auth-user.ts (1)
  • authenticateUserInOpenOpsTables (10-30)
packages/openops/src/lib/openops-tables/reset-user-password.ts (1)
  • resetUserPassword (4-17)
packages/server/api/src/app/user/user-service.ts (1)
  • userService (27-286)
🔇 Additional comments (14)
deploy/helm/openops/values.yaml (1)

93-93: Helm values version bump is consistent and correct.

The tag bump from "0.2.10" to "0.2.11" aligns with configuration across other deployment files. The MIGRATE_ON_STARTUP: 'true' setting (line 106) will handle any required database migrations automatically during container startup.

deploy/docker-compose/docker-compose.yml (1)

24-24: Docker Compose version references are consistent.

Both occurrences of the version bump (environment variable on line 24 and image tag on line 50) are aligned and correct. The application will automatically migrate the database on startup (line 63), ensuring schema compatibility with the new version.

Also applies to: 50-50

compose.yaml (1)

5-5: Compose file version bump is consistent across all deployment configs.

All three deployment configurations (compose.yaml, docker-compose.yml, and helm values.yaml) use the same tag 0.2.11, ensuring consistency across development and production environments.

packages/server/api/src/app/database/postgres-connection.ts (1)

41-41: LGTM!

The migration is correctly imported and registered at the end of the migrations list, maintaining chronological order.

Also applies to: 88-88

packages/server/api/src/app/database/migrations/1763755045436-MigrateTablesUserPassword.ts (1)

14-20: LGTM!

The early return when no users exist is a good optimization. The query for passwords aligns with the PR's objective to synchronize hashed passwords to OpenOps Tables.

packages/server/api/src/app/authentication/new-user/create-user.ts (1)

121-126: LGTM - Hashed password now passed to OpenOps Tables.

This change passes the hashed password (user.password) to OpenOps Tables instead of the plain text input. This aligns with the PR objective and requires the OpenOps Tables 0.2.11 upgrade to be deployed alongside this change.

Ensure the OpenOps Tables upgrade to 0.2.11 is deployed before or simultaneously with this change, as the Tables service must now accept hashed passwords during user creation.

packages/server/api/src/app/database/seeds/seed-admin.ts (2)

81-85: LGTM - Properly syncs hashed password to OpenOps Tables after admin creation.

The flow correctly authenticates with the plain text password first (required for initial auth), then resets the password in OpenOps Tables to the hashed version for subsequent authentications.


172-179: LGTM - Password update correctly syncs to OpenOps Tables.

The sequence is correct: update locally (which hashes the password), authenticate with the plain text password to get a token, then reset the Tables password to the hashed version.

packages/server/api/src/app/authentication/basic/authentication-service.ts (1)

39-42: LGTM - Sign-in now uses hashed password for OpenOps Tables authentication.

After the local password verification succeeds (line 34-37), the hashed password from the database is used to authenticate with OpenOps Tables. This is consistent with the new password synchronization flow.

packages/server/api/src/app/openops-tables/auth-admin-tables.ts (1)

24-32: LGTM - Admin authentication now uses hashed password from database.

The change correctly retrieves the admin user from the database and uses the stored hashed password for OpenOps Tables authentication. This aligns with the broader password synchronization strategy in this PR.

Consider the upgrade path: existing installations where the admin user exists in OpenOps Tables but the password wasn't yet synced (hashed) will need the seed process to run first to establish the hashed password in Tables. Verify that deployment procedures account for this ordering.

packages/server/api/test/unit/openops-tables/auth-admin-tables.test.ts (3)

22-27: LGTM! Mock setup correctly added.

The mock for getUserByEmailOrFail is properly configured to support the new authentication flow.


39-45: LGTM! User mock properly initialized.

The user object with id and password fields correctly supports the new flow where admin credentials come from the user record.


10-10: LGTM! Mock updated to reflect system property changes.

Replacing OPENOPS_ADMIN_PASSWORD with DB_TYPE correctly reflects the removal of the admin password from system configuration.

packages/server/api/src/app/user/user-service.ts (1)

191-205: All callers properly handle the new Promise<User> return type.

Verification complete: The only call site of updatePassword is in packages/server/api/src/app/database/seeds/seed-admin.ts (line 173), which correctly captures the return value as updatedUser and actively uses it on line 177 to access updatedUser.password. The signature change has been properly propagated and all call sites are compatible with the new return type.

@sonarqubecloud
Copy link
Copy Markdown

@MarceloRGonc MarceloRGonc merged commit 50bd33b into main Nov 26, 2025
24 checks passed
@MarceloRGonc MarceloRGonc deleted the mg/OPS-3123 branch November 26, 2025 16:19
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.

4 participants