Skip to content

Conversation

@zensgit
Copy link
Owner

@zensgit zensgit commented Sep 24, 2025

Summary

  • De-duplicate Makefile hooks target and scope test-rust to API tests (keeps CI green while jive-core tests are unfinished).
  • Gate sensitive login debug prints under cfg(debug_assertions).
  • Fix currency_service unwrap_or_* on non-Option fields.
  • Add integration tests: unauthorized export and cross-family forbidden.
  • Refresh API .sqlx metadata after migrations.

Testing

  • make api-lint passes (SQLx offline strict + Clippy -D warnings).
  • API tests: 24/24 passing with SQLX_OFFLINE=true.
  • make test passes (API + Flutter).

Notes

  • jive-core integration tests remain unfinished; test-rust intentionally runs API tests only.

@Copilot Copilot AI review requested due to automatic review settings September 24, 2025 08:43
@gemini-code-assist
Copy link

Summary of Changes

Hello @zensgit, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a series of improvements across the API and build system, focusing on development workflow efficiency, security, and data integrity. Key changes include refining Makefile targets for better test management and SQLx operations, enhancing login security by conditionally compiling debug logs, and fixing data handling logic in currency and family services. Additionally, new integration tests have been added to validate API export authorization, and a database migration ensures superadmin baseline consistency.

Highlights

  • Makefile Enhancements: Streamlined Makefile targets by de-duplicating hooks, scoping Rust tests specifically to the API, and adding new targets for SQLx metadata preparation and API development with core export.
  • Security & Debugging: Gated sensitive login debug prints behind cfg(debug_assertions) to prevent information leakage in production builds and improved password verification to support both Argon2 and bcrypt hash formats.
  • Data Consistency Fixes: Corrected currency_service to directly use non-nullable symbol and base_currency fields, and updated family_service to properly assign owner_id during family creation and created_by for default ledgers.
  • API Export Testing: Introduced new integration tests to ensure unauthorized access and cross-family data export attempts are correctly rejected with 401 and 403 status codes, respectively.
  • Database Migration: Added a new migration (027_fix_superadmin_baseline.sql) to normalize superadmin baseline data for consistent login and permissions.
  • SQLx Metadata Refresh: Updated .sqlx metadata files across jive-api and jive-core to reflect schema changes and ensure offline mode compatibility.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@zensgit zensgit enabled auto-merge (squash) September 24, 2025 08:44
Copy link

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 addresses maintenance tasks for the Jive API: adding authentication/authorization tests for export functionality, improving security by gating debug logs, fixing service bugs, and streamlining development workflow.

  • Adds integration tests for unauthorized export and cross-family forbidden scenarios
  • Gates sensitive authentication debug output behind debug_assertions
  • Fixes unwrap_or_* usage on non-Option fields in currency service

Reviewed Changes

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

Show a summary per file
File Description
jive-api/tests/integration/transactions_export_unauthorized_test.rs New test ensuring export endpoints require authentication
jive-api/tests/integration/transactions_export_forbidden_test.rs New test verifying cross-family access control for exports
jive-api/src/services/family_service.rs Updates SQL insert statements to match schema changes
jive-api/src/services/currency_service.rs Fixes incorrect unwrap_or_* usage on non-null fields
jive-api/src/handlers/enhanced_profile.rs Adds name field to user registration SQL
jive-api/src/handlers/auth.rs Gates debug logging behind cfg(debug_assertions) and improves password verification
jive-api/migrations/027_fix_superadmin_baseline.sql New migration normalizing superadmin baseline data
README.md Adds documentation for local development ports and hooks
Makefile Updates test target scope and adds new development targets
Files not reviewed (4)
  • jive-api/.sqlx/query-7cc5d220abdcf4ef2e63aa86b9ce0d947460192ba4f0e6d62150dc1d62557cdf.json: Language not supported
  • jive-api/.sqlx/query-d9740c18a47d026853f7b8542fe0f3b90ec7a106b9277dcb40fe7bcef98e7bf7.json: Language not supported
  • jive-api/.sqlx/query-f17a00d3f66b7b8b0caf3f09c537719a175f66d73ed5a5d4b8739fe1c159bd83.json: Language not supported
  • jive-core/.sqlx/query-bc9ca6ac70ce59f1015fa7d5dab9d99b87e3411c3e80405cc102888c694dd792.json: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

.bind(user_id)
.bind(&req.email)
.bind(&req.name)
.bind(&req.name)
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

The same field req.name is being bound twice consecutively. This appears to be an error where both name and full_name columns are being set to the same value. Based on the INSERT statement, the second bind should likely be &req.full_name or a different field.

Suggested change
.bind(&req.name)
.bind(&req.full_name)

Copilot uses AI. Check for mistakes.
Comment on lines 214 to 220
if cfg!(debug_assertions) {
println!(
"DEBUG[login]: query_by_email={}, input={}",
query_by_email,
&login_input
);
}
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

Debug logging the raw login input could expose sensitive information like usernames in logs. Consider redacting or hashing the input for security, even in debug builds.

Copilot uses AI. Check for mistakes.
family_id,
// base_currency 可能为可空;兜底为 CNY
base_currency: settings.base_currency.unwrap_or_else(|| "CNY".to_string()),
// base_currency 在当前查询中为非空列,直接使用
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

[nitpick] The comment contains Chinese text which may not be appropriate for code comments in an English codebase. Consider translating to English: '// base_currency is non-null in current query, use directly'

Suggested change
// base_currency 在当前查询中为非空列,直接使用
// base_currency is non-null in current query, use directly

Copilot uses AI. Check for mistakes.
- Remove duplicate hooks target; keep a single canonical implementation
- Change make test-rust to run jive-api tests only (SQLX_OFFLINE=true)
- Keeps CI green while jive-core tests are unfinished
@zensgit zensgit force-pushed the chore/api-export-auth-tests-makefile-20250924 branch from 2385613 to 7089d57 Compare September 24, 2025 08:45
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several useful changes, including de-duplicating Makefile targets, adding integration tests for the export API, gating sensitive logs, and fixing several minor issues in the API services. The changes improve code quality, security, and test coverage.

My review focuses on a couple of areas for improvement:

  • A Makefile command that could hide errors.
  • A new integration test that appears to have a logical flaw in its implementation.

Overall, this is a solid contribution. Please see my detailed comments.

Comment on lines +20 to +60
async fn export_cross_family_forbidden() {
let pool = create_test_pool().await;

// Create two users and two families (each user owns their own family)
let user_a = create_test_user(&pool).await;
let user_b = create_test_user(&pool).await;
let family_a = create_test_family(&pool, user_a.id).await;
let family_b = create_test_family(&pool, user_b.id).await;

// Token for user A bound to family A
let token_a_family_a = bearer_for(user_a.id, family_a.id).await;

// Minimal router with CSV export endpoint
let app = Router::new()
.route("/api/v1/transactions/export.csv", get(export_transactions_csv_stream))
.with_state(pool.clone());

// Try to export for family B while token is bound to family A.
// The handler reads family_id from token claims, not from query, so the
// access check should fail before any data is returned.
let req = Request::builder()
.method("GET")
.uri("/api/v1/transactions/export.csv")
.header(header::AUTHORIZATION, token_a_family_a)
.body(Body::empty())
.unwrap();

let resp = app.clone().oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);

// Control: user B exporting their own family's data should pass auth (may be empty CSV)
let token_b_family_b = bearer_for(user_b.id, family_b.id).await;
let req = Request::builder()
.method("GET")
.uri("/api/v1/transactions/export.csv")
.header(header::AUTHORIZATION, token_b_family_b)
.body(Body::empty())
.unwrap();
let resp = app.clone().oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
}

Choose a reason for hiding this comment

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

high

This test appears to have a logical flaw. It creates a valid token for user_a in family_a (token_a_family_a) and then asserts that a request with this token results in StatusCode::FORBIDDEN. A valid request from a user for their own family's data should result in StatusCode::OK, as correctly asserted in the control case for user_b.

The test's name export_cross_family_forbidden and the associated comment suggest the intent is to verify that a user from one family cannot access another family's data. To test this correctly, you should attempt to make a request for family_b's data using user_a's identity.

I've suggested a rewrite that correctly implements the intended cross-family access check and also makes the control case more direct by having user A access their own family's data.

    async fn export_cross_family_forbidden() {
        let pool = create_test_pool().await;

        // Create two users and two families (each user owns their own family)
        let user_a = create_test_user(&pool).await;
        let user_b = create_test_user(&pool).await;
        let family_a = create_test_family(&pool, user_a.id).await;
        let family_b = create_test_family(&pool, user_b.id).await;

        // Minimal router with CSV export endpoint
        let app = Router::new()
            .route("/api/v1/transactions/export.csv", get(export_transactions_csv_stream))
            .with_state(pool.clone());

        // Test case: User A tries to export data for User B's family (should be forbidden)
        // A token is created for User A, but associated with Family B.
        // The backend should detect that User A is not a member of Family B and deny access.
        let token_a_for_family_b = bearer_for(user_a.id, family_b.id).await;
        let req = Request::builder()
            .method("GET")
            .uri("/api/v1/transactions/export.csv")
            .header(header::AUTHORIZATION, token_a_for_family_b)
            .body(Body::empty())
            .unwrap();

        let resp = app.clone().oneshot(req).await.unwrap();
        assert_eq!(resp.status(), StatusCode::FORBIDDEN);

        // Control Case: User A exporting their own family's data should be allowed
        let token_a_for_family_a = bearer_for(user_a.id, family_a.id).await;
        let req = Request::builder()
            .method("GET")
            .uri("/api/v1/transactions/export.csv")
            .header(header::AUTHORIZATION, token_a_for_family_a)
            .body(Body::empty())
            .unwrap();
        let resp = app.clone().oneshot(req).await.unwrap();
        assert_eq!(resp.status(), StatusCode::OK);
    }

sqlx-prepare-core:
@echo "准备 jive-core SQLx 元数据 (features=server,db)..."
@echo "确保数据库与迁移就绪 (优先 5433)..."
@cd jive-api && DB_PORT=$${DB_PORT:-5433} ./scripts/migrate_local.sh --force || true

Choose a reason for hiding this comment

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

medium

The || true at the end of this command will suppress any errors from the migrate_local.sh script, causing the make target to succeed even if the migration fails. This can hide issues like a database connection problem and lead to a confusing state where subsequent steps fail without a clear root cause. It's better to let the script fail and report the error, which makes debugging easier.

	@cd jive-api && DB_PORT=$${DB_PORT:-5433} ./scripts/migrate_local.sh --force

zensgit and others added 4 commits September 24, 2025 17:07
- Fix formatting issues in auth.rs debug prints
- Update SQLx offline cache to match current queries

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update .sqlx cache to match CI-generated schema validation
- Fixes SQLx offline cache validation failures in CI

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@zensgit zensgit merged commit 90e0427 into main Sep 24, 2025
10 checks passed
@zensgit zensgit deleted the chore/api-export-auth-tests-makefile-20250924 branch September 24, 2025 11:44
@zensgit zensgit mentioned this pull request Oct 15, 2025
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.

1 participant