Commit Graph
551 Commits
Author SHA1 Message Date
Herculino Trotta b10897e64b fix(updates): keep update banner across page loads and restarts
The update check shared the file-based default cache with cachalot, whose culling randomly deleted the key as pages loaded. Store only the latest release in a dedicated cache, compare it to APP_VERSION at render time, and drop the startup delete. Hide the banner when CHECK_FOR_UPDATES is off or APP_VERSION isn't a valid version.
2026-11-02 17:24:28 -03:00
Herculino Trotta 5b98779070 test(auth): add positive controls for local login and tighten basic auth check
- Add flag-off login page and admin password tests so the refusal tests can't pass for unrelated reasons
- Expect exactly 401 from refused basic auth
- Gate OIDC_DISABLE_LOCAL_LOGIN on an explicit OIDC_CONFIGURED flag
- Realign the README OIDC table, use "email" consistently, and warn about lockout with OIDC_ALLOW_SIGNUP=false
2026-11-02 16:41:52 -03:00
fariskazmi 161fc19721 feat(auth): add OIDC_DISABLE_LOCAL_LOGIN to turn off password logins
When the flag is on and an OIDC provider is configured, every e-mail and
password check is refused: the login page, the admin and the API's basic
auth. OIDC logins, API tokens, OAuth apps and permission checks keep working.
The login page hides the password form.
2026-11-02 09:08:30 -06:00
Herculino Trotta 3716b9b0e3 feat(transactions): link transfer sides and show them as a single item
Transfers are still two transactions, now linked to each other:
- shown as one item in lists, with each side's amount, account,
  category and tags ("Combine transfers" user setting, on by default)
- date, reference date, paid and mute stay in sync between sides
- edit opens the transfer form; delete, restore, duplicate and bulk
  actions act on both sides
- transfers made by the transfer form now trigger rules for each side

Recurring and quick transactions gain a Transfer type, with a "To
account" and optional "To amount", generating linked transfers. A
transfer can be converted into either.

Existing transfers are left as they are, since there is no reliable
way to pair them.
2026-11-02 01:14:46 -03:00
Herculino Trotta 1d22c9ff66 feat(demo): generate demo data relative to today
Replace the hard-dated demo fixture with a load_demo_data command that
builds the dataset around the current date, so the demo always has data
in the current month. It also covers more features: multi-currency
accounts with exchange rate history, an ETF and Bitcoin tracked with DCA
strategies, transfers, a credit card using reference dates, installment
plans, recurring transactions, quick transactions, rules and filter
presets.
2026-11-01 00:13:01 -03:00
Herculino Trotta 809564a1d3 Merge pull request #634 from eitchtee/feat/reconciliation-cutoff
feat(accounts): reconcile balances against a date or reference month cutoff
2026-10-31 18:00:23 -03:00
Herculino Trotta fcd666946f feat(accounts): reconcile balances against a date or reference month cutoff
Credit cards often have paid transactions dated after the invoice being
closed, which made reconciliation against all paid transactions wrong.

The reconciliation panel now lets you balance against all paid
transactions (previous behavior), paid transactions up to a date, or
paid transactions up to a reference month. The adjustment transaction is
dated at the cutoff so later reconciliations with the same or a later
cutoff include it. An invalid cutoff blocks the reconciliation instead
of silently falling back.
2026-10-31 17:56:36 -03:00
Herculino Trotta 5468d17e20 ci: provide container paths and a clean TMPDIR for tests
test(transactions): enable soft delete explicitly in the hard-delete attachment test
2026-10-24 14:01:47 -03:00
Herculino Trotta d7537b54ad Merge pull request #629 from eitchtee/fix/import-object-authorization
fix(import): enforce object-level authorization on import profiles and runs
2026-10-24 13:56:53 -03:00
Herculino Trotta 4446db3249 test(users): stop using SocialLogin.state_from_request removed in allauth 65.20
django-allauth 65.20 removed SocialLogin.state_from_request, breaking the
OIDC email auto-connect tests after the bump in #627.
2026-10-24 13:27:10 -03:00
Herculino Trotta 1e486a608e fix(import): enforce object-level authorization on import profiles and runs
ImportProfile and ImportRun had no owner, and neither the web views nor the
REST viewsets filtered by the requesting user, so any authenticated user could
read, edit and delete every other user's import profiles and runs
(GHSA-q668-h56g-mfp4).

- ImportProfile is now a SharedObject, with share and take-ownership views
  like transaction rules; the global unique constraint on name is dropped.
- ImportRun is now an OwnedObject owned by the uploader, so runs made with a
  shared profile stay private to whoever ran them.
- Existing runs are backfilled with the owner of their imported
  transactions; existing profiles stay unowned and usable by everyone.
2026-10-24 13:26:37 -03:00
obervinov aa95ade7ce fix(rules): write set_mute action to mute instead of is_paid 2026-10-21 21:51:22 +04:00
Herculino Trotta 3261e5f266 fix(transactions): apply the form amount to a paid transaction converted into an installment plan 2026-10-12 02:41:14 -03:00
Herculino Trotta 35ef1dd716 fix(transactions): apply the form amount to a paid transaction converted into a recurring transaction 2026-10-12 02:41:14 -03:00
Herculino Trotta 5f71288e44 Merge pull request #612 from fxmacrodata/fxmacrodata-hardening
fix(automatic-exchange-rates): don't follow redirects or log the key in the FXMacroData provider
2026-10-10 17:52:17 -03:00
Herculino Trotta 92c33a9fcc Merge pull request #615 from eitchtee/fix/oidc-unverified-email-autoconnect
feat(auth): make OIDC email trust configurable
2026-10-10 17:51:37 -03:00
Herculino Trotta d1bb9698a0 Merge pull request #614 from eitchtee/feat/simulation-tool
feat: add Simulator tool
2026-10-10 17:51:24 -03:00
Herculino Trotta c274e92f86 Merge pull request #613 from eitchtee/feat/transaction-convert-menu
feat(transactions): convert a transaction into a quick transaction, recurring transaction or installment plan
2026-10-10 17:51:13 -03:00
Herculino Trotta 2544820e1a Merge pull request #569 from eitchtee/copilot/fix-code-scanning-alerts
Stop exposing exception text in OAuth dynamic client registration errors
2026-10-10 17:50:53 -03:00
Herculino Trotta e925822574 fix(oauth): keep specific DCR errors without exposing exception text
Parsing helpers now return (value, error) instead of raising, so no
exception object flows into the response while clients keep the
precise messages they had before.

full_clean() failures map to fixed per-field messages; redirect URI
errors surface as NON_FIELD_ERRORS from Application.clean().

Also reject non-string token_endpoint_auth_method with a 400 instead
of crashing with a TypeError.
2026-10-10 17:48:14 -03:00
Herculino Trotta 59de9f2443 feat(auth): configure OIDC email trust
Allow verified-only linking with OIDC_TRUST_EMAIL=false while retaining case-insensitive matching. Keep unconditional trust enabled by default for compatibility with #518; that mode retains the documented takeover risk.
2026-10-10 17:01:52 -03:00
Herculino Trotta 6e72594ec7 test(simulator): cover baseline calculation and page 2026-10-10 15:26:59 -03:00
Herculino Trotta bdf0cf8d4c feat(simulator): add simulator tool page
Hyperscript-driven page to simulate one-time, recurring and installment expenses or incomes over the projected balance.
2026-10-10 15:26:59 -03:00
Herculino Trotta 81e572c8ab feat(simulator): add projected baseline calculation
Per-currency monthly net changes from active, tracked accounts, plus the exchange rate for currencies with an exchange currency.
2026-10-10 15:26:59 -03:00
Herculino Trotta 195da63998 feat(transactions): add Convert submenu to the transaction actions menu
Replaces the "Add as quick transaction" action.
2026-10-09 23:25:20 -03:00
Herculino Trotta 8c5c299312 feat(transactions): add views to convert a transaction into a quick transaction, recurring transaction or installment plan 2026-10-09 23:25:19 -03:00
Herculino Trotta cd6069f857 feat(transactions): let new recurring transactions and installment plans adopt an existing transaction 2026-10-09 23:25:19 -03:00
Robert Tidball 237f8efc6c fix(automatic-exchange-rates): don't follow redirects or log the key in the FXMacroData provider 2026-10-05 21:45:20 +11:00
Robert Tidball 1c3c29d366 feat(automatic-exchange-rates): add FXMacroData as a provider 2026-10-01 15:33:26 +10:00
Herculino Trotta a2ee0b4ae6 feat(transactions): update cuelume; bring back improved volume user setting 2026-09-13 01:24:37 -03:00
Herculino Trotta 82211567d0 feat(insights): replace fixed overviews with a single arrangeable overview
Categories, Tags and Entities Overview are merged into one Overview page.
Levels are chips that can be dragged (or moved with the arrow keys) to set
the hierarchy and clicked to switch off, so any ordering of the three is
reachable from the same page.

Only the results reload on change; the controls stay in place. The chip
order is kept in the session and defaults to categories by tags, matching
the old Categories Overview.
2026-09-12 18:02:24 -03:00
Herculino Trotta 3a5e688a90 feat(insights): add tags and entities overview 2026-09-12 14:31:12 -03:00
Herculino Trotta 52fe81f564 feat(ui:transaction): deleting transactions is smoother and no longer requires a full list refresh 2026-09-12 13:45:41 -03:00
Herculino Trotta e2f26b3629 fix(sharing): enforce object-level authorization outside the rules app
The rules endpoints were one instance of a pattern repeated across every
SharedObject-backed app. Route the rest through the same helper.

DCA entries were the worst case and are strictly wider than the reported
rules bug: DCAEntry has an unscoped default manager, so filtering by
strategy__id alone reached entries on strategies the caller could not see
at all. No public or shared strategy was needed -- only a guessable
integer. strategy_entry_add/edit/delete now resolve through the parent
strategy with via="strategy".

Every SharedObject delete view carried an inverted condition:

    if obj.owner != request.user and request.user in obj.shared_with.all():
        obj.shared_with.remove(request.user)
    else:
        obj.delete()

An object its owner had made public matched neither branch's intent and
fell through to delete(), so any authenticated user could destroy it.
Confirmed reachable for accounts, account groups, categories, tags,
entities and DCA strategies. The owner now deletes, a shared user revokes
only their own access, and anyone else gets a 403.

The API viewsets had no object-level check at all. DjangoModelPermissions
gated them shut for ordinary users, who hold no model permissions, so this
was not reachable in a default install -- but the check belongs there
regardless, and a user granted change_account in the admin could write any
visible account. SharedObjectPermission adds it for the SharedObject
viewsets, leaving reads to SharedObjectManager.

account_toggle_untracked only flips the calling user's own row in the
untracked_by m2m, so it takes READ rather than EDIT.

Refs GHSA-83g9-vjqf-2j5q
2026-09-01 23:15:41 -03:00
Herculino Trotta 039ad225d3 test(rules): cover object-level authorization for transaction rules
Regression tests for GHSA-83g9-vjqf-2j5q, one per endpoint the advisory
named plus the delete and view paths found alongside them: a non-owner
gets 403 on every mutation of a public or shared rule, and the object is
asserted unchanged afterwards.

Also covers the parts that are easy to regress in the other direction:
shared users keep read access, a shared user deleting only revokes their
own access, unowned rules stay claimable, and children of invisible rules
answer 404 rather than 403.

SharedObjectPredicateParityTests asserts is_visible_to agrees with
SharedObjectManager across every owner/visibility/shared combination.
The manager builds a Q and the predicate tests an instance, so they
cannot share an implementation and can otherwise drift apart.
2026-09-01 21:43:13 -03:00
Herculino Trotta 18d4ab7d11 fix(rules): enforce object-level authorization on rule endpoints
SharedObjectManager scopes querysets to what a user may see, which
includes other people's public and shared-with-them objects. Several
mutating rule endpoints treated that visibility as permission to write.

Generalise get_owned_object_or_403 into get_shared_object_or_error, which
takes an explicit access level instead of inferring one:

- READ requires the object to be visible; denial is 404 so the response
  does not confirm that an id exists.
- EDIT requires ownership; denial is 403, but only after the visibility
  check, so 403 never leaks the existence of an invisible object. This
  matters for TransactionRuleAction, whose manager is unscoped.

The previous owner_path resolved to a User and discarded the object, so
it could not express visibility at all. via= now points at the governing
SharedObject and is resolved with a plain getattr, so an unresolvable
path raises instead of silently granting access.

is_visible_to/is_editable_by replace is_accessible_by, which was never
called and tested visibility == "shared", a value that does not exist in
Visibility.

Also fixes two further holes in the same module:

- transaction_rule_delete fell through to delete() whenever the caller
  was not in shared_with, so any user could delete a public rule. Now
  only the owner deletes; a shared user revokes their own access.
- transaction_rule_view was read-only but is now explicitly READ, so
  rules shared with a user stay viewable.

The activate/deactivate control is hidden for rules the user does not
own, instead of rendering a button that always fails.

Refs GHSA-83g9-vjqf-2j5q
2026-09-01 21:25:46 -03:00
Moshe Levi 68a9286ce5 Fix BOLA on transaction-rule endpoints via get_owned_object_or_403
Add apps.common.functions.get_owned_object_or_403, an ownership-enforcing
variant of get_object_or_404, and apply it across every rules handler that
resolved a TransactionRule / (UpdateOrCreate)TransactionRuleAction from a URL id
without an owner check. Mirrors the check in transaction_rule_edit
(obj.owner and obj.owner != request.user); objects with no owner remain
accessible, preserving existing behaviour. Nested ownership (actions owned via
their parent rule) is handled with owner_path='rule.owner'.
2026-08-31 20:14:40 +03:00
Herculino Trotta cd48edddab feat(transactions): add htmx filter presets 2026-08-16 01:40:11 -03:00
Herculino Trotta 9e135560fe feat(transactions): add filter preset model 2026-08-16 01:40:03 -03:00
Herculino Trotta 8e09f3e8d8 feat(net-worth): add consolidated chart lines 2026-08-15 14:36:13 -03:00
Herculino Trotta ea3e9fd68f feat(monthly): show asset accounts/currencies on specific sumaries 2026-08-15 01:30:57 -03:00
Herculino Trotta 2ab6621205 Merge pull request #581 from obervinov/fix/recurring-tags-scheduler
fix: recurring tags and entities dropped when generated for another user
2026-08-13 00:00:48 -03:00
obervinov 11a6b3faa6 fix(transactions): recurring tags and entities dropped when generated for another user 2026-08-12 22:33:01 +04:00
copilot-swe-agent[bot] f7bf96eef5 fix: avoid exposing validation details in oauth client registration errors 2026-07-18 18:57:15 +00:00
Herculino Trotta 743951a862 feat(automatic-exchange-rates): Add Yahoo Finance as a provider via yfinance 2026-07-18 15:42:51 -03:00
Herculino Trotta 5a80a3b1d3 feat: replace mp3 sounds with cuelume; deprecate volume setting 2026-07-17 21:28:13 -03:00
obervinovandClaude Opus 4.8 ca14f77f41 test: cover demo-mode block on revoked-token delete
Parity with the existing demo-mode tests for token create/revoke.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 00:00:10 +04:00
obervinovandClaude Opus 4.8 0fb37a59fa feat: add delete button for revoked API tokens
Revoked tokens previously stayed in the list with no way to remove them.
Adds a delete action (hard delete, scoped to the owner, gated behind
demo mode) shown on revoked rows, alongside the existing revoke action on
active ones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 23:55:38 +04:00
Herculino Trotta 106d721279 feat: add demo mode tests to ensure API is disabled on it 2026-06-27 18:02:31 -03:00
Herculino Trotta d0e9c05283 feat: disable oauth and token creation while on demo mode 2026-06-27 18:02:03 -03:00