From 8446356cc62e1311b08627b22cf16a4e46c414c6 Mon Sep 17 00:00:00 2001 From: Darin Loh <43445720+talkintomato@users.noreply.github.com> Date: Sun, 15 Dec 2024 20:27:39 +0800 Subject: [PATCH 1/9] fix: space missing on create local account copy (#3985) --- .../src/components/modals/CreateAccountModal.tsx | 2 +- upcoming-release-notes/3985.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 upcoming-release-notes/3985.md diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx index 6ba131bf6dd..bb73dc2a3d5 100644 --- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx @@ -204,7 +204,7 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { - {t('Create a local account')} + {t('Create a local account')}{' '} {t( 'if you want to add transactions manually. You can also', )}{' '} diff --git a/upcoming-release-notes/3985.md b/upcoming-release-notes/3985.md new file mode 100644 index 00000000000..827aec6910b --- /dev/null +++ b/upcoming-release-notes/3985.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [talkintomato] +--- + +Fix space missing on create local account copy From 1165c4c008071d6fe047c184a5fd702e96c7e098 Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Mon, 16 Dec 2024 20:20:21 +0000 Subject: [PATCH 2/9] Update bug-report.yml (#3992) --- .github/ISSUE_TEMPLATE/bug-report.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index b6d881e7731..24c04fbbacc 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -23,8 +23,6 @@ body: options: - label: 'I have searched and found no existing issue' required: true - - label: 'I will be providing steps how to reproduce the bug (in most cases this will also mean uploading a demo budget file)' - required: true validations: required: true - type: textarea @@ -36,6 +34,14 @@ body: value: 'A bug happened!' validations: required: true + - type: textarea + id: reproduction + attributes: + label: How can we reproduce the issue? + description: Please give step-by-step instructions on how to reproduce the issue. In most cases this might also require uploading a sample budget/import file. + value: '' + validations: + required: true - type: markdown id: env-info attributes: From b6fbcef6f0a4cb8e8218233be1ac20e08c37e70b Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Mon, 16 Dec 2024 20:21:15 +0000 Subject: [PATCH 3/9] Update bug-report.yml --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 24c04fbbacc..9a53dc6ed45 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -39,7 +39,7 @@ body: attributes: label: How can we reproduce the issue? description: Please give step-by-step instructions on how to reproduce the issue. In most cases this might also require uploading a sample budget/import file. - value: '' + value: ' ' validations: required: true - type: markdown From 94666a2ac175786a2e9b79f49bc75672bd6de34d Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Mon, 16 Dec 2024 20:21:44 +0000 Subject: [PATCH 4/9] Update bug-report.yml --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 9a53dc6ed45..c61a9eedf23 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -39,7 +39,7 @@ body: attributes: label: How can we reproduce the issue? description: Please give step-by-step instructions on how to reproduce the issue. In most cases this might also require uploading a sample budget/import file. - value: ' ' + value: 'How can we reproduce the issue?' validations: required: true - type: markdown From 11bde73fa547151356b7abcb010ac72795b2889c Mon Sep 17 00:00:00 2001 From: Matt Fiddaman Date: Wed, 18 Dec 2024 09:05:46 +0000 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=94=A7=20upgrade=20better-sqlite3=20(?= =?UTF-8?q?#3987)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * upgrade better-sqlite3 * note --- packages/api/package.json | 2 +- packages/desktop-electron/package.json | 2 +- packages/loot-core/package.json | 4 ++-- upcoming-release-notes/3987.md | 6 ++++++ yarn.lock | 24 ++++++++++++------------ 5 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 upcoming-release-notes/3987.md diff --git a/packages/api/package.json b/packages/api/package.json index 4f9c4adc949..0e0f2f3d5a3 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -23,7 +23,7 @@ }, "dependencies": { "@actual-app/crdt": "workspace:^", - "better-sqlite3": "^9.6.0", + "better-sqlite3": "^11.7.0", "compare-versions": "^6.1.0", "node-fetch": "^3.3.2", "uuid": "^9.0.1" diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index ced422a10fc..2e84496302c 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -85,7 +85,7 @@ "npmRebuild": false }, "dependencies": { - "better-sqlite3": "^9.6.0", + "better-sqlite3": "^11.7.0", "fs-extra": "^11.2.0", "promise-retry": "^2.0.1" }, diff --git a/packages/loot-core/package.json b/packages/loot-core/package.json index f92b6e6cb44..cd8257cba33 100644 --- a/packages/loot-core/package.json +++ b/packages/loot-core/package.json @@ -23,7 +23,7 @@ "@rschedule/standard-date-adapter": "^1.5.0", "absurd-sql": "0.0.54", "adm-zip": "^0.5.10", - "better-sqlite3": "^9.6.0", + "better-sqlite3": "^11.7.0", "csv-parse": "^4.16.3", "csv-stringify": "^5.6.5", "date-fns": "^2.30.0", @@ -45,7 +45,7 @@ "@swc/helpers": "^0.5.11", "@swc/jest": "^0.2.36", "@types/adm-zip": "^0.5.0", - "@types/better-sqlite3": "^7.6.8", + "@types/better-sqlite3": "^7.6.12", "@types/jest": "^27.5.2", "@types/jlongster__sql.js": "npm:@types/sql.js@latest", "@types/pegjs": "^0.10.3", diff --git a/upcoming-release-notes/3987.md b/upcoming-release-notes/3987.md new file mode 100644 index 00000000000..65b048f57e4 --- /dev/null +++ b/upcoming-release-notes/3987.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [matt-fidd] +--- + +Upgrade better-sqlite3 to v11.7 diff --git a/yarn.lock b/yarn.lock index e39f9406bc8..f6a4ad2ab0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,7 +28,7 @@ __metadata: "@swc/jest": "npm:^0.2.36" "@types/jest": "npm:^27.5.2" "@types/uuid": "npm:^9.0.2" - better-sqlite3: "npm:^9.6.0" + better-sqlite3: "npm:^11.7.0" compare-versions: "npm:^6.1.0" jest: "npm:^27.5.1" node-fetch: "npm:^3.3.2" @@ -5306,12 +5306,12 @@ __metadata: languageName: node linkType: hard -"@types/better-sqlite3@npm:^7.6.8": - version: 7.6.8 - resolution: "@types/better-sqlite3@npm:7.6.8" +"@types/better-sqlite3@npm:^7.6.12": + version: 7.6.12 + resolution: "@types/better-sqlite3@npm:7.6.12" dependencies: "@types/node": "npm:*" - checksum: 10/404e9b7210564866b0f8878353cc6a16c6ffb313077cbb5aec6176ad2b0a30f64236f03f0a40d36d86bf4eab7658bdcd6d6a8a65dc377de7910fc9e9932885a4 + checksum: 10/a442231518f1a3e28e0ee6efe2581807e5cfaa88a8af4513da34e6ae303ce82f5666e8a0048a1d2738fa248b1b369ee28e59a75189ae0b43e28e44a243a2f24b languageName: node linkType: hard @@ -7010,14 +7010,14 @@ __metadata: languageName: node linkType: hard -"better-sqlite3@npm:^9.6.0": - version: 9.6.0 - resolution: "better-sqlite3@npm:9.6.0" +"better-sqlite3@npm:^11.7.0": + version: 11.7.0 + resolution: "better-sqlite3@npm:11.7.0" dependencies: bindings: "npm:^1.5.0" node-gyp: "npm:latest" prebuild-install: "npm:^7.1.1" - checksum: 10/06b3d95221071a06c2e22a9746d9b7049c0bce7962e5e3290ccf088fffbf4d4d52868f0d98b8ae2565fe33b1adab89823145f23c6f6eb63ecc4fc1b883f9082c + checksum: 10/a09bb28c0292bb7c037896ee99197815841275bca2d14f63b58994188239f292642c8c7ea3e0206d8ea6c7530d7b03d7343138ebeb9a4cc855c0b3663e00c812 languageName: node linkType: hard @@ -8690,7 +8690,7 @@ __metadata: "@electron/rebuild": "npm:3.6.0" "@types/copyfiles": "npm:^2" "@types/fs-extra": "npm:^11" - better-sqlite3: "npm:^9.6.0" + better-sqlite3: "npm:^11.7.0" copyfiles: "npm:^2.4.1" cross-env: "npm:^7.0.3" electron: "npm:30.0.6" @@ -13367,7 +13367,7 @@ __metadata: "@swc/helpers": "npm:^0.5.11" "@swc/jest": "npm:^0.2.36" "@types/adm-zip": "npm:^0.5.0" - "@types/better-sqlite3": "npm:^7.6.8" + "@types/better-sqlite3": "npm:^7.6.12" "@types/jest": "npm:^27.5.2" "@types/jlongster__sql.js": "npm:@types/sql.js@latest" "@types/pegjs": "npm:^0.10.3" @@ -13378,7 +13378,7 @@ __metadata: absurd-sql: "npm:0.0.54" adm-zip: "npm:^0.5.10" assert: "npm:^2.1.0" - better-sqlite3: "npm:^9.6.0" + better-sqlite3: "npm:^11.7.0" browserify-zlib: "npm:^0.2.0" buffer: "npm:^6.0.3" cross-env: "npm:^7.0.3" From 4ce5e2fd075434bbe8f03f6c5983896a74c4cde2 Mon Sep 17 00:00:00 2001 From: Matt Fiddaman Date: Wed, 18 Dec 2024 21:55:23 +0000 Subject: [PATCH 6/9] Prevent schedules with null amounts from crashing the app (#3958) * test * note * add comment --- packages/loot-core/src/shared/schedules.ts | 4 ++++ upcoming-release-notes/3958.md | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 upcoming-release-notes/3958.md diff --git a/packages/loot-core/src/shared/schedules.ts b/packages/loot-core/src/shared/schedules.ts index b1b0b991160..78f6b82e947 100644 --- a/packages/loot-core/src/shared/schedules.ts +++ b/packages/loot-core/src/shared/schedules.ts @@ -276,6 +276,10 @@ export function getScheduledAmount( amount: number | { num1: number; num2: number }, inverse: boolean = false, ): number { + // this check is temporary, and required at the moment as a schedule rule + // allows the amount condition to be deleted which causes a crash + if (amount == null) return 0; + if (typeof amount === 'number') { return inverse ? -amount : amount; } diff --git a/upcoming-release-notes/3958.md b/upcoming-release-notes/3958.md new file mode 100644 index 00000000000..13a7478f90b --- /dev/null +++ b/upcoming-release-notes/3958.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [matt-fidd] +--- + +Prevent schedules with null amounts from crashing the app From 6cfb9d2a7a3cf23854d1cda03d729531edad5b8d Mon Sep 17 00:00:00 2001 From: Matt Fiddaman Date: Thu, 19 Dec 2024 15:13:44 +0000 Subject: [PATCH 7/9] Fix incorrect boldening of synced accounts in the sidebar (#4009) --- packages/loot-core/src/server/main.ts | 2 +- upcoming-release-notes/4009.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 upcoming-release-notes/4009.md diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts index 89a29484f7a..059869d935d 100644 --- a/packages/loot-core/src/server/main.ts +++ b/packages/loot-core/src/server/main.ts @@ -1056,7 +1056,7 @@ function handleSyncResponse( newTransactions.push(...added); matchedTransactions.push(...updated); - if (added.length > 0 || updated.length > 0) { + if (added.length > 0) { updatedAccounts.push(acct.id); } } diff --git a/upcoming-release-notes/4009.md b/upcoming-release-notes/4009.md new file mode 100644 index 00000000000..7ee3d60b65c --- /dev/null +++ b/upcoming-release-notes/4009.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [matt-fidd] +--- + +Fix incorrect boldening of synced accounts in the sidebar From cde81da72c214ee5b068fa487e5a715e5f2dbffb Mon Sep 17 00:00:00 2001 From: youngcw Date: Sat, 21 Dec 2024 19:49:29 -0700 Subject: [PATCH 8/9] [Goals]: Fix applying templates in tracking budget (#4010) * fix logic * note --- packages/loot-core/src/server/budget/goaltemplates.ts | 2 +- upcoming-release-notes/4010.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 upcoming-release-notes/4010.md diff --git a/packages/loot-core/src/server/budget/goaltemplates.ts b/packages/loot-core/src/server/budget/goaltemplates.ts index f10cd35fa80..9e4ba9e9251 100644 --- a/packages/loot-core/src/server/budget/goaltemplates.ts +++ b/packages/loot-core/src/server/budget/goaltemplates.ts @@ -130,7 +130,7 @@ async function processTemplate( const isReflect = isReflectBudget(); const categoriesLong = await getCategories(); categoriesLong.forEach(c => { - if (!isReflect && !c.is_income) { + if (isReflect || !c.is_income) { categories.push(c); } }); diff --git a/upcoming-release-notes/4010.md b/upcoming-release-notes/4010.md new file mode 100644 index 00000000000..a4809b9b956 --- /dev/null +++ b/upcoming-release-notes/4010.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [youngcw] +--- + +Fix goal templates not applying in tracking budget From 0b2c8ccd8872d625efd268b97f4df0f19c8f1313 Mon Sep 17 00:00:00 2001 From: lelemm Date: Mon, 23 Dec 2024 13:46:22 -0300 Subject: [PATCH 9/9] OpenId Implementation (#3878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * OpenId implementation * Code rabbit auto generated code applied Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Code rabbit suggestions round 2 Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fixes from code rabbit round 1 * fixes from code rabbit round 2 * change variable name * code review round 3 * Update VRT * small fix * Update VRT * linter * app.tsx * LoggedInUser * UserAccess * UserAccessHeader * UserAccessPage * UserAccessRow * UserDirectory * UserDirectoryHeader * UserDirectoryPage * UserDirectoryRow * BudgetList * Bootstrap * Login * OpenIdForm * CreateAccountModal * EditAccess * EditUser * GoCardlessInitialiseModal * OpenIDEnableModal * PasswordEnableModal * SimpleFinInitialiseModal * TransferOwnership * AuthSettings * fix hooks in EditUser * enable electron openid login * typecheck * linter and typecheck fixes * Update VRT * small fix * linter * small changes for file owner name and a fix for privacyfilter in the username * linter for merge * change the entra url and changing the electron loopback url when built * "logged in as" was showing when had no user * linter * linterĀ² * code review --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: matt --- packages/api/methods.ts | 5 +- ...ecks-the-page-visuals-1-chromium-linux.png | Bin 83063 -> 83213 bytes ...ecks-the-page-visuals-3-chromium-linux.png | Bin 80538 -> 80700 bytes .../desktop-client/src/auth/AuthProvider.tsx | 48 ++ .../src/auth/ProtectedRoute.tsx | 65 +++ packages/desktop-client/src/auth/types.ts | 3 + .../src/browser-preload.browser.js | 2 + .../desktop-client/src/components/App.tsx | 22 +- .../src/components/FinancesApp.tsx | 31 +- .../src/components/LoggedInUser.tsx | 145 +++++- .../src/components/ManageRules.tsx | 2 +- .../desktop-client/src/components/Modals.tsx | 41 ++ .../src/components/ServerContext.tsx | 89 +++- .../admin/UserAccess/UserAccess.tsx | 292 ++++++++++++ .../admin/UserAccess/UserAccessHeader.tsx | 16 + .../admin/UserAccess/UserAccessPage.tsx | 22 + .../admin/UserAccess/UserAccessRow.tsx | 148 ++++++ .../admin/UserDirectory/UserDirectory.tsx | 370 +++++++++++++++ .../UserDirectory/UserDirectoryHeader.tsx | 34 ++ .../admin/UserDirectory/UserDirectoryPage.tsx | 49 ++ .../admin/UserDirectory/UserDirectoryRow.tsx | 144 ++++++ .../autocomplete/PayeeAutocomplete.test.tsx | 25 +- .../src/components/common/Button.tsx | 10 +- .../src/components/common/Button2.tsx | 14 +- .../{rules => common}/SimpleTable.tsx | 2 +- .../src/components/manager/BudgetList.tsx | 256 +++++++++- .../src/components/manager/ManagementApp.tsx | 42 +- .../manager/subscribe/Bootstrap.tsx | 22 +- .../manager/subscribe/ConfirmPasswordForm.tsx | 86 +++- .../components/manager/subscribe/Login.tsx | 350 +++++++++----- .../manager/subscribe/OpenIdCallback.ts | 16 + .../manager/subscribe/OpenIdForm.tsx | 448 ++++++++++++++++++ .../components/manager/subscribe/common.tsx | 30 +- .../components/modals/CreateAccountModal.tsx | 269 ++++++----- .../src/components/modals/EditAccess.tsx | 154 ++++++ .../src/components/modals/EditUser.tsx | 415 ++++++++++++++++ .../modals/GoCardlessInitialiseModal.tsx | 46 +- .../components/modals/OpenIDEnableModal.tsx | 111 +++++ .../components/modals/PasswordEnableModal.tsx | 145 ++++++ .../modals/SimpleFinInitialiseModal.tsx | 20 +- .../components/modals/TransferOwnership.tsx | 206 ++++++++ .../src/components/responsive/wide.ts | 2 + .../src/components/settings/AuthSettings.tsx | 95 ++++ .../src/components/settings/Experimental.tsx | 6 + .../src/components/settings/index.tsx | 2 + .../transactions/TransactionsTable.test.jsx | 57 +-- .../src/hooks/useFeatureFlag.ts | 1 + .../src/hooks/useSyncServerStatus.ts | 2 +- packages/desktop-client/src/index.tsx | 5 +- packages/desktop-client/vite.config.mts | 7 + packages/desktop-electron/index.ts | 48 ++ packages/desktop-electron/preload.ts | 3 +- .../loot-core/src/client/reducers/budgets.ts | 8 + .../loot-core/src/client/shared-listeners.ts | 12 + .../src/client/state-types/modals.d.ts | 19 + packages/loot-core/src/server/admin/app.ts | 191 ++++++++ .../src/server/admin/types/handlers.ts | 44 ++ packages/loot-core/src/server/api-models.ts | 2 + .../loot-core/src/server/cloud-storage.ts | 52 ++ packages/loot-core/src/server/main.ts | 184 ++++++- packages/loot-core/src/server/post.ts | 96 ++++ packages/loot-core/src/shared/errors.ts | 51 ++ packages/loot-core/src/types/budget.d.ts | 1 + packages/loot-core/src/types/file.d.ts | 7 + packages/loot-core/src/types/handlers.d.ts | 2 + .../loot-core/src/types/models/index.d.ts | 1 + .../loot-core/src/types/models/openid.d.ts | 7 + packages/loot-core/src/types/models/user.ts | 30 ++ .../loot-core/src/types/models/userAccess.ts | 10 + packages/loot-core/src/types/prefs.d.ts | 5 +- .../loot-core/src/types/server-handlers.d.ts | 74 ++- packages/loot-core/typings/window.d.ts | 1 + upcoming-release-notes/3878.md | 6 + 73 files changed, 4835 insertions(+), 391 deletions(-) create mode 100644 packages/desktop-client/src/auth/AuthProvider.tsx create mode 100644 packages/desktop-client/src/auth/ProtectedRoute.tsx create mode 100644 packages/desktop-client/src/auth/types.ts create mode 100644 packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx create mode 100644 packages/desktop-client/src/components/admin/UserAccess/UserAccessHeader.tsx create mode 100644 packages/desktop-client/src/components/admin/UserAccess/UserAccessPage.tsx create mode 100644 packages/desktop-client/src/components/admin/UserAccess/UserAccessRow.tsx create mode 100644 packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx create mode 100644 packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryHeader.tsx create mode 100644 packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryPage.tsx create mode 100644 packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryRow.tsx rename packages/desktop-client/src/components/{rules => common}/SimpleTable.tsx (96%) create mode 100644 packages/desktop-client/src/components/manager/subscribe/OpenIdCallback.ts create mode 100644 packages/desktop-client/src/components/manager/subscribe/OpenIdForm.tsx create mode 100644 packages/desktop-client/src/components/modals/EditAccess.tsx create mode 100644 packages/desktop-client/src/components/modals/EditUser.tsx create mode 100644 packages/desktop-client/src/components/modals/OpenIDEnableModal.tsx create mode 100644 packages/desktop-client/src/components/modals/PasswordEnableModal.tsx create mode 100644 packages/desktop-client/src/components/modals/TransferOwnership.tsx create mode 100644 packages/desktop-client/src/components/settings/AuthSettings.tsx create mode 100644 packages/loot-core/src/server/admin/app.ts create mode 100644 packages/loot-core/src/server/admin/types/handlers.ts create mode 100644 packages/loot-core/src/types/models/openid.d.ts create mode 100644 packages/loot-core/src/types/models/user.ts create mode 100644 packages/loot-core/src/types/models/userAccess.ts create mode 100644 upcoming-release-notes/3878.md diff --git a/packages/api/methods.ts b/packages/api/methods.ts index a6dec0e743a..5ed53e9c7ec 100644 --- a/packages/api/methods.ts +++ b/packages/api/methods.ts @@ -86,7 +86,10 @@ export function addTransactions( } export function importTransactions(accountId, transactions) { - return send('api/transactions-import', { accountId, transactions }); + return send('api/transactions-import', { + accountId, + transactions, + }); } export function getTransactions(accountId, startDate, endDate) { diff --git a/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png index f4ebfe318ad64d03f329b56917f2e8d8d8b909ca..18d6f7f5e579d3129b9e543a5eb1e47c9bad32c3 100644 GIT binary patch literal 83213 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

7@W5 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%0DQ5a6*@~zk2@9&k6p4U%q{9YHqF!d@#J1L`r&( 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 diff --git a/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png b/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png index 8d85b9cf4c3a303c0baece5126d21125eeed2b5b..38ed8bfcb43c8fcc1457d46dd3281275875d8e4f 100644 GIT binary patch literal 80700 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�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 literal 80538 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

;Z8G}>b@YTTfr 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 G+y4WEFelUi diff --git a/packages/desktop-client/src/auth/AuthProvider.tsx b/packages/desktop-client/src/auth/AuthProvider.tsx new file mode 100644 index 00000000000..e0d5903783d --- /dev/null +++ b/packages/desktop-client/src/auth/AuthProvider.tsx @@ -0,0 +1,48 @@ +import React, { createContext, useContext, type ReactNode } from 'react'; +import { useSelector } from 'react-redux'; + +import { type State } from 'loot-core/client/state-types'; + +import { useServerURL } from '../components/ServerContext'; + +import { type Permissions } from './types'; + +type AuthContextType = { + hasPermission: (permission?: Permissions) => boolean; +}; + +const AuthContext = createContext(undefined); + +type AuthProviderProps = { + children?: ReactNode; +}; + +export const AuthProvider = ({ children }: AuthProviderProps) => { + const userData = useSelector((state: State) => state.user.data); + const serverUrl = useServerURL(); + + const hasPermission = (permission?: Permissions) => { + if (!permission) { + return true; + } + + return ( + !serverUrl || + userData?.permission?.toUpperCase() === permission?.toUpperCase() + ); + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; diff --git a/packages/desktop-client/src/auth/ProtectedRoute.tsx b/packages/desktop-client/src/auth/ProtectedRoute.tsx new file mode 100644 index 00000000000..5dacd055782 --- /dev/null +++ b/packages/desktop-client/src/auth/ProtectedRoute.tsx @@ -0,0 +1,65 @@ +import { useEffect, useState, type ReactElement } from 'react'; +import { useSelector } from 'react-redux'; + +import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file'; + +import { View } from '../components/common/View'; +import { useMetadataPref } from '../hooks/useMetadataPref'; + +import { useAuth } from './AuthProvider'; +import { type Permissions } from './types'; + +type ProtectedRouteProps = { + permission: Permissions; + element: ReactElement; + validateOwner?: boolean; +}; + +export const ProtectedRoute = ({ + element, + permission, + validateOwner, +}: ProtectedRouteProps) => { + const { hasPermission } = useAuth(); + const [permissionGranted, setPermissionGranted] = useState(false); + const [cloudFileId] = useMetadataPref('cloudFileId'); + const allFiles = useSelector(state => state.budgets.allFiles || []); + const remoteFiles = allFiles.filter( + (f): f is SyncedLocalFile | RemoteFile => + f.state === 'remote' || f.state === 'synced' || f.state === 'detached', + ); + const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); + const userData = useSelector(state => state.user.data); + + useEffect(() => { + const hasRequiredPermission = hasPermission(permission); + setPermissionGranted(hasRequiredPermission); + + if (!hasRequiredPermission && validateOwner) { + if (currentFile) { + setPermissionGranted( + currentFile.usersWithAccess.some(u => u.userId === userData?.userId), + ); + } + } + }, [ + cloudFileId, + permission, + validateOwner, + hasPermission, + currentFile, + userData, + ]); + + return permissionGranted ? ( + element + ) : ( + +

You don't have permission to view this page

+ + ); +}; diff --git a/packages/desktop-client/src/auth/types.ts b/packages/desktop-client/src/auth/types.ts new file mode 100644 index 00000000000..7c88e304d7e --- /dev/null +++ b/packages/desktop-client/src/auth/types.ts @@ -0,0 +1,3 @@ +export enum Permissions { + ADMINISTRATOR = 'ADMIN', +} diff --git a/packages/desktop-client/src/browser-preload.browser.js b/packages/desktop-client/src/browser-preload.browser.js index 95c048247c8..3f12e472942 100644 --- a/packages/desktop-client/src/browser-preload.browser.js +++ b/packages/desktop-client/src/browser-preload.browser.js @@ -81,6 +81,8 @@ global.Actual = { }); }, + startOAuthServer: () => {}, + restartElectronServer: () => {}, openFileDialog: async ({ filters = [] }) => { diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx index 4c5895c52eb..431bed29fb4 100644 --- a/packages/desktop-client/src/components/App.tsx +++ b/packages/desktop-client/src/components/App.tsx @@ -9,7 +9,7 @@ import { } from 'react-error-boundary'; import { HotkeysProvider } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { @@ -20,12 +20,14 @@ import { sync, } from 'loot-core/client/actions'; import { SpreadsheetProvider } from 'loot-core/client/SpreadsheetProvider'; +import { type State } from 'loot-core/client/state-types'; import * as Platform from 'loot-core/src/client/platform'; import { init as initConnection, send, } from 'loot-core/src/platform/client/fetch'; +import { useActions } from '../hooks/useActions'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { installPolyfills } from '../polyfills'; import { styles, hasHiddenScrollbars, ThemeStyle, useTheme } from '../style'; @@ -49,6 +51,8 @@ function AppInner() { const { t } = useTranslation(); const { showBoundary: showErrorBoundary } = useErrorBoundary(); const dispatch = useDispatch(); + const userData = useSelector((state: State) => state.user.data); + const { signOut, addNotification } = useActions(); const maybeUpdate = async (cb?: () => T): Promise => { if (global.Actual.isUpdateReadyForDownload()) { @@ -123,6 +127,22 @@ function AppInner() { global.Actual.updateAppMenu(budgetId); }, [budgetId]); + useEffect(() => { + if (userData?.tokenExpired) { + addNotification({ + type: 'error', + id: 'login-expired', + title: t('Login expired'), + sticky: true, + message: t('Login expired, please login again.'), + button: { + title: t('Go to login'), + action: signOut, + }, + }); + } + }, [userData, userData?.tokenExpired]); + return budgetId ? : ; } diff --git a/packages/desktop-client/src/components/FinancesApp.tsx b/packages/desktop-client/src/components/FinancesApp.tsx index 6983b2708be..8b987636ee3 100644 --- a/packages/desktop-client/src/components/FinancesApp.tsx +++ b/packages/desktop-client/src/components/FinancesApp.tsx @@ -14,6 +14,8 @@ import { addNotification, sync } from 'loot-core/client/actions'; import { type State } from 'loot-core/src/client/state-types'; import * as undo from 'loot-core/src/platform/client/undo'; +import { ProtectedRoute } from '../auth/ProtectedRoute'; +import { Permissions } from '../auth/types'; import { useAccounts } from '../hooks/useAccounts'; import { useLocalPref } from '../hooks/useLocalPref'; import { useMetaThemeColor } from '../hooks/useMetaThemeColor'; @@ -21,6 +23,7 @@ import { useNavigate } from '../hooks/useNavigate'; import { theme } from '../style'; import { getIsOutdated, getLatestVersion } from '../util/versions'; +import { UserAccessPage } from './admin/UserAccess/UserAccessPage'; import { BankSyncStatus } from './BankSyncStatus'; import { View } from './common/View'; import { GlobalKeys } from './GlobalKeys'; @@ -34,7 +37,9 @@ import { Reports } from './reports'; import { LoadingIndicator } from './reports/LoadingIndicator'; import { NarrowAlternate, WideComponent } from './responsive'; import { useResponsive } from './responsive/ResponsiveProvider'; +import { UserDirectoryPage } from './responsive/wide'; import { ScrollProvider } from './ScrollProvider'; +import { useMultiuserEnabled } from './ServerContext'; import { Settings } from './settings'; import { FloatableSidebar } from './sidebar'; import { Titlebar } from './Titlebar'; @@ -93,6 +98,8 @@ export function FinancesApp() { 'flags.updateNotificationShownForVersion', ); + const multiuserEnabled = useMultiuserEnabled(); + useEffect(() => { // Wait a little bit to make sure the sync button will get the // sync start event. This can be improved later. @@ -281,7 +288,29 @@ export function FinancesApp() { } /> - + {multiuserEnabled && ( + } + /> + } + /> + )} + {multiuserEnabled && ( + } + /> + } + /> + )} {/* redirect all other traffic to the budget page */} } /> diff --git a/packages/desktop-client/src/components/LoggedInUser.tsx b/packages/desktop-client/src/components/LoggedInUser.tsx index bba3b2e885a..982b1ae9b42 100644 --- a/packages/desktop-client/src/components/LoggedInUser.tsx +++ b/packages/desktop-client/src/components/LoggedInUser.tsx @@ -1,11 +1,15 @@ -// @ts-strict-ignore import React, { useState, useEffect, useRef, type CSSProperties } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; import { closeBudget, getUserData, signOut } from 'loot-core/client/actions'; import { type State } from 'loot-core/src/client/state-types'; +import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file'; +import { useAuth } from '../auth/AuthProvider'; +import { Permissions } from '../auth/types'; +import { useMetadataPref } from '../hooks/useMetadataPref'; import { useNavigate } from '../hooks/useNavigate'; import { theme, styles } from '../style'; @@ -14,13 +18,15 @@ import { Menu } from './common/Menu'; import { Popover } from './common/Popover'; import { Text } from './common/Text'; import { View } from './common/View'; -import { useServerURL } from './ServerContext'; +import { PrivacyFilter } from './PrivacyFilter'; +import { useMultiuserEnabled, useServerURL } from './ServerContext'; type LoggedInUserProps = { hideIfNoServer?: boolean; style?: CSSProperties; color?: string; }; + export function LoggedInUser({ hideIfNoServer, style, @@ -33,7 +39,18 @@ export function LoggedInUser({ const [loading, setLoading] = useState(true); const [menuOpen, setMenuOpen] = useState(false); const serverUrl = useServerURL(); - const triggerRef = useRef(null); + const triggerRef = useRef(null); + const [budgetId] = useMetadataPref('id'); + const [cloudFileId] = useMetadataPref('cloudFileId'); + const location = useLocation(); + const { hasPermission } = useAuth(); + const multiuserEnabled = useMultiuserEnabled(); + const allFiles = useSelector(state => state.budgets.allFiles || []); + const remoteFiles = allFiles.filter( + f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', + ) as (SyncedLocalFile | RemoteFile)[]; + const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); + const hasSyncedPrefs = useSelector((state: State) => state.prefs.synced); useEffect(() => { async function init() { @@ -52,7 +69,7 @@ export function LoggedInUser({ navigate('/change-password'); } - async function onMenuSelect(type) { + const handleMenuSelect = async (type: string) => { setMenuOpen(false); switch (type) { @@ -63,6 +80,15 @@ export function LoggedInUser({ await onCloseBudget(); navigate('/login'); break; + case 'user-access': + navigate('/user-access'); + break; + case 'user-directory': + navigate('/user-directory'); + break; + case 'index': + navigate('/'); + break; case 'sign-out': dispatch(signOut()); break; @@ -71,8 +97,9 @@ export function LoggedInUser({ navigate('/config-server'); break; default: + break; } - } + }; function serverMessage() { if (!serverUrl) { @@ -86,9 +113,7 @@ export function LoggedInUser({ return t('Server online'); } - if (hideIfNoServer && !serverUrl) { - return null; - } + if (hideIfNoServer && !serverUrl) return null; if (loading && serverUrl) { return ( @@ -105,16 +130,101 @@ export function LoggedInUser({ ); } + type MenuItem = { + name: string; + text: string; + }; + + const getMenuItems = (): (MenuItem | typeof Menu.line)[] => { + const isAdmin = hasPermission(Permissions.ADMINISTRATOR); + + const baseMenu: (MenuItem | typeof Menu.line)[] = []; + if ( + serverUrl && + !userData?.offline && + userData?.loginMethod === 'password' + ) { + baseMenu.push({ name: 'change-password', text: t('Change password') }); + } + if (serverUrl) { + baseMenu.push({ name: 'sign-out', text: t('Sign out') }); + } + baseMenu.push({ + name: 'config-server', + text: serverUrl ? t('Change server URL') : t('Start using a server'), + }); + + const adminMenu: (MenuItem | typeof Menu.line)[] = []; + if (multiuserEnabled && isAdmin) { + if (!budgetId && location.pathname !== '/') { + adminMenu.push({ name: 'index', text: t('View file list') }); + } else if ( + serverUrl && + !userData?.offline && + location.pathname !== '/user-directory' + ) { + adminMenu.push({ name: 'user-directory', text: t('User Directory') }); + } + } + + if ( + multiuserEnabled && + ((currentFile && userData && currentFile.owner === userData.userId) || + isAdmin) && + serverUrl && + !userData?.offline && + cloudFileId && + location.pathname !== '/user-access' + ) { + adminMenu.push({ + name: 'user-access', + text: t('User Access Management'), + }); + } + + if (adminMenu.length > 0) { + adminMenu.push(Menu.line); + } + + return [...adminMenu, ...baseMenu]; + }; + return ( + {!loading && + multiuserEnabled && + userData && + userData?.displayName && + !hasSyncedPrefs && ( + + + (logged in as: {userData?.displayName}) + + + )} + {!loading && + multiuserEnabled && + userData && + userData?.displayName && + hasSyncedPrefs && ( + + + (logged in as:{' '} + + {userData?.displayName} + + ) + + + )} setMenuOpen(false)} > diff --git a/packages/desktop-client/src/components/ManageRules.tsx b/packages/desktop-client/src/components/ManageRules.tsx index f27660eb438..36643423ba5 100644 --- a/packages/desktop-client/src/components/ManageRules.tsx +++ b/packages/desktop-client/src/components/ManageRules.tsx @@ -30,12 +30,12 @@ import { theme } from '../style'; import { Button } from './common/Button2'; import { Link } from './common/Link'; import { Search } from './common/Search'; +import { SimpleTable } from './common/SimpleTable'; import { Stack } from './common/Stack'; import { Text } from './common/Text'; import { View } from './common/View'; import { RulesHeader } from './rules/RulesHeader'; import { RulesList } from './rules/RulesList'; -import { SimpleTable } from './rules/SimpleTable'; function mapValue( field, diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index 58ba6f051f4..f0c008c141e 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -28,8 +28,10 @@ import { CoverModal } from './modals/CoverModal'; import { CreateAccountModal } from './modals/CreateAccountModal'; import { CreateEncryptionKeyModal } from './modals/CreateEncryptionKeyModal'; import { CreateLocalAccountModal } from './modals/CreateLocalAccountModal'; +import { EditUserAccess } from './modals/EditAccess'; import { EditFieldModal } from './modals/EditFieldModal'; import { EditRuleModal } from './modals/EditRuleModal'; +import { EditUserFinanceApp } from './modals/EditUser'; import { EnvelopeBalanceMenuModal } from './modals/EnvelopeBalanceMenuModal'; import { EnvelopeBudgetMenuModal } from './modals/EnvelopeBudgetMenuModal'; import { EnvelopeBudgetMonthMenuModal } from './modals/EnvelopeBudgetMonthMenuModal'; @@ -54,7 +56,9 @@ import { ImportYNAB5Modal } from './modals/manager/ImportYNAB5Modal'; import { ManageRulesModal } from './modals/ManageRulesModal'; import { MergeUnusedPayeesModal } from './modals/MergeUnusedPayeesModal'; import { NotesModal } from './modals/NotesModal'; +import { OpenIDEnableModal } from './modals/OpenIDEnableModal'; import { OutOfSyncMigrationsModal } from './modals/OutOfSyncMigrationsModal'; +import { PasswordEnableModal } from './modals/PasswordEnableModal'; import { PayeeAutocompleteModal } from './modals/PayeeAutocompleteModal'; import { ScheduledTransactionMenuModal } from './modals/ScheduledTransactionMenuModal'; import { SelectLinkedAccountsModal } from './modals/SelectLinkedAccountsModal'; @@ -65,6 +69,7 @@ import { TrackingBudgetMenuModal } from './modals/TrackingBudgetMenuModal'; import { TrackingBudgetMonthMenuModal } from './modals/TrackingBudgetMonthMenuModal'; import { TrackingBudgetSummaryModal } from './modals/TrackingBudgetSummaryModal'; import { TransferModal } from './modals/TransferModal'; +import { TransferOwnership } from './modals/TransferOwnership'; import { DiscoverSchedules } from './schedules/DiscoverSchedules'; import { PostsOfflineNotification } from './schedules/PostsOfflineNotification'; import { ScheduleDetails } from './schedules/ScheduleDetails'; @@ -615,9 +620,45 @@ export function Modals() { return ; case 'import-actual': return ; + case 'manager-load-backup': + return ( + + ); case 'out-of-sync-migrations': return ; + case 'edit-access': + return ( + + ); + + case 'edit-user': + return ( + + ); + + case 'transfer-ownership': + return ; + + case 'enable-openid': + return ; + + case 'enable-password-auth': + return ; + default: throw new Error('Unknown modal'); } diff --git a/packages/desktop-client/src/components/ServerContext.tsx b/packages/desktop-client/src/components/ServerContext.tsx index a3db98801ea..c32ed2ac518 100644 --- a/packages/desktop-client/src/components/ServerContext.tsx +++ b/packages/desktop-client/src/components/ServerContext.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import React, { createContext, useState, @@ -8,26 +7,64 @@ import React, { type ReactNode, } from 'react'; +import { t } from 'i18next'; + +import { addNotification } from 'loot-core/client/actions'; import { send } from 'loot-core/src/platform/client/fetch'; +import { type Handlers } from 'loot-core/types/handlers'; + +type LoginMethods = { + method: string; + displayName: string; + active: boolean; +}; type ServerContextValue = { url: string | null; version: string; + multiuserEnabled: boolean; + availableLoginMethods: LoginMethods[]; setURL: ( url: string, opts?: { validate?: boolean }, ) => Promise<{ error?: string }>; + refreshLoginMethods: () => Promise; + setMultiuserEnabled: (enabled: boolean) => void; + setLoginMethods: (methods: LoginMethods[]) => void; }; const ServerContext = createContext({ url: null, version: '', + multiuserEnabled: false, + availableLoginMethods: [], setURL: () => Promise.reject(new Error('ServerContext not initialized')), + refreshLoginMethods: () => + Promise.reject(new Error('ServerContext not initialized')), + setMultiuserEnabled: () => {}, + setLoginMethods: () => {}, }); export const useServerURL = () => useContext(ServerContext).url; export const useServerVersion = () => useContext(ServerContext).version; export const useSetServerURL = () => useContext(ServerContext).setURL; +export const useMultiuserEnabled = () => { + const { multiuserEnabled } = useContext(ServerContext); + const loginMethod = useLoginMethod(); + return multiuserEnabled && loginMethod === 'openid'; +}; + +export const useLoginMethod = () => { + const availableLoginMethods = useContext(ServerContext).availableLoginMethods; + + if (!availableLoginMethods || availableLoginMethods.length === 0) { + return 'password'; + } + + return availableLoginMethods.filter(m => m.active)[0]?.method ?? 'password'; +}; +export const useAvailableLoginMethods = () => + useContext(ServerContext).availableLoginMethods; async function getServerVersion() { const result = await send('get-server-version'); @@ -37,9 +74,22 @@ async function getServerVersion() { return ''; } +export const useRefreshLoginMethods = () => + useContext(ServerContext).refreshLoginMethods; + +export const useSetMultiuserEnabled = () => + useContext(ServerContext).setMultiuserEnabled; + +export const useSetLoginMethods = () => + useContext(ServerContext).setLoginMethods; + export function ServerProvider({ children }: { children: ReactNode }) { const [serverURL, setServerURL] = useState(''); const [version, setVersion] = useState(''); + const [multiuserEnabled, setMultiuserEnabled] = useState(false); + const [availableLoginMethods, setAvailableLoginMethods] = useState< + LoginMethods[] + >([]); useEffect(() => { async function run() { @@ -49,6 +99,38 @@ export function ServerProvider({ children }: { children: ReactNode }) { run(); }, []); + const refreshLoginMethods = useCallback(async () => { + if (serverURL) { + const data: Awaited> = + await send('subscribe-get-login-methods'); + if ('error' in data) { + addNotification({ + type: 'error', + title: t('Failed to refresh login methods'), + message: data.error ?? t('Unknown'), + }); + setAvailableLoginMethods([]); + } else if (data.methods) { + setAvailableLoginMethods(data.methods); + } else { + setAvailableLoginMethods([]); + } + } + }, [serverURL]); + + useEffect(() => { + if (serverURL) { + send('subscribe-needs-bootstrap').then( + (data: Awaited>) => { + if ('hasServer' in data && data.hasServer) { + setAvailableLoginMethods(data.availableLoginMethods); + setMultiuserEnabled(data.multiuser); + } + }, + ); + } + }, [serverURL]); + const setURL = useCallback( async (url: string, opts: { validate?: boolean } = {}) => { const { error } = await send('set-server-url', { ...opts, url }); @@ -65,8 +147,13 @@ export function ServerProvider({ children }: { children: ReactNode }) { {children} diff --git a/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx b/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx new file mode 100644 index 00000000000..5183f86bded --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx @@ -0,0 +1,292 @@ +// @ts-strict-ignore +import React, { + useState, + useEffect, + useCallback, + useMemo, + type SetStateAction, + type Dispatch, + type CSSProperties, +} from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; + +import { addNotification, pushModal } from 'loot-core/client/actions'; +import { send } from 'loot-core/src/platform/client/fetch'; +import * as undo from 'loot-core/src/platform/client/undo'; +import { type Handlers } from 'loot-core/types/handlers'; +import { type UserAvailable } from 'loot-core/types/models'; +import { type UserAccessEntity } from 'loot-core/types/models/userAccess'; + +import { useMetadataPref } from '../../../hooks/useMetadataPref'; +import { SvgLockOpen } from '../../../icons/v1'; +import { SvgLockClosed } from '../../../icons/v2'; +import { theme } from '../../../style'; +import { Button } from '../../common/Button2'; +import { Link } from '../../common/Link'; +import { Search } from '../../common/Search'; +import { SimpleTable } from '../../common/SimpleTable'; +import { Text } from '../../common/Text'; +import { View } from '../../common/View'; + +import { UserAccessHeader } from './UserAccessHeader'; +import { UserAccessRow } from './UserAccessRow'; + +type ManageUserAccessContentProps = { + isModal: boolean; + setLoading?: Dispatch>; +}; + +function UserAccessContent({ + isModal, + setLoading, +}: ManageUserAccessContentProps) { + const { t } = useTranslation(); + + const [allAccess, setAllAccess] = useState([]); + const [page, setPage] = useState(0); + const [filter, setFilter] = useState(''); + const [cloudFileId] = useMetadataPref('cloudFileId'); + + const filteredAccesses = useMemo( + () => + (filter === '' + ? allAccess + : allAccess.filter( + access => + access?.displayName + .toLowerCase() + .includes(filter.toLowerCase()) ?? false, + ) + ).slice(0, 100 + page * 50), + [allAccess, filter, page], + ); + const [hoveredUserAccess, setHoveredUserAccess] = useState(null); + + const onSearchChange = useCallback( + (value: string) => { + setFilter(value); + setPage(0); + }, + [setFilter], + ); + + const loadAccess = useCallback(async () => { + setLoading(true); + const data: Awaited> = + await send('access-get-available-users', cloudFileId as string); + + const sortUsers = (a: UserAvailable, b: UserAvailable) => { + if ((a.owner ?? 0) !== (b.owner ?? 0)) { + return (b.owner ?? 0) - (a.owner ?? 0); + } + return (a.displayName ?? '').localeCompare(b.displayName ?? ''); + }; + + if ('error' in data) { + addNotification({ + type: 'error', + id: 'error', + title: t('Error getting available users'), + sticky: true, + message: data.error, + }); + return []; + } + + const loadedAccess = data + .map(user => ({ + ...user, + displayName: user.displayName || user.userName, + })) + .sort(sortUsers); + + setAllAccess(loadedAccess); + return loadedAccess; + }, [cloudFileId, setLoading, t]); + + const loadOwner = useCallback(async () => { + const file: Awaited> = + (await send('get-user-file-info', cloudFileId as string)) ?? {}; + const owner = file?.usersWithAccess.filter(user => user.owner); + + if (owner.length > 0) { + return owner[0]; + } + + return null; + }, [cloudFileId]); + + useEffect(() => { + async function loadData() { + try { + await loadAccess(); + } catch (error) { + console.error('Error loading user access data:', error); + } finally { + setLoading(false); + } + } + + loadData(); + + return () => { + undo.setUndoState('openModal', null); + }; + }, [setLoading, loadAccess, loadOwner]); + + function loadMore() { + setPage(page => page + 1); + } + + const onHover = useCallback(id => { + setHoveredUserAccess(id); + }, []); + + return ( + + + + + + Determine which users can view and manage your budgets..{' '} + + Learn more + + + + + + + + + + + + + + + + { + await loadAccess(); + setLoading(false); + }} + /> + + + ); +} + +type ManageUsersProps = { + isModal: boolean; + setLoading?: Dispatch>; +}; + +export function UserAccess({ + isModal, + setLoading = () => {}, +}: ManageUsersProps) { + return ; +} + +type UsersAccessListProps = { + accesses: UserAccessEntity[]; + hoveredAccess?: string; + onHover?: (id: string | null) => void; +}; + +function UserAccessList({ + accesses, + hoveredAccess, + onHover, +}: UsersAccessListProps) { + if (accesses.length === 0) { + return null; + } + + return ( + + {accesses.map(access => { + const hovered = hoveredAccess === access.userId; + + return ( + + ); + })} + + ); +} + +type LockToggleProps = { + style: CSSProperties; + onToggleSave: () => void; +}; + +function LockToggle({ style, onToggleSave }: LockToggleProps) { + const [hover, setHover] = useState(false); + const dispatch = useDispatch(); + + return ( + + ); +} diff --git a/packages/desktop-client/src/components/admin/UserAccess/UserAccessHeader.tsx b/packages/desktop-client/src/components/admin/UserAccess/UserAccessHeader.tsx new file mode 100644 index 00000000000..66f0a330643 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccessHeader.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Cell, TableHeader } from '../../table'; + +export function UserAccessHeader() { + const { t } = useTranslation(); + + return ( + + + + + + ); +} diff --git a/packages/desktop-client/src/components/admin/UserAccess/UserAccessPage.tsx b/packages/desktop-client/src/components/admin/UserAccess/UserAccessPage.tsx new file mode 100644 index 00000000000..3760768e48c --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccessPage.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Page } from '../../Page'; + +import { UserAccess } from './UserAccess'; + +export function UserAccessPage() { + const { t } = useTranslation(); + + return ( + + + + ); +} diff --git a/packages/desktop-client/src/components/admin/UserAccess/UserAccessRow.tsx b/packages/desktop-client/src/components/admin/UserAccess/UserAccessRow.tsx new file mode 100644 index 00000000000..7473156df60 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccessRow.tsx @@ -0,0 +1,148 @@ +// @ts-strict-ignore +import React, { memo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { send } from 'loot-core/platform/client/fetch'; +import { getUserAccessErrors } from 'loot-core/shared/errors'; +import { type UserAvailable } from 'loot-core/types/models'; + +import { useActions } from '../../../hooks/useActions'; +import { useMetadataPref } from '../../../hooks/useMetadataPref'; +import { theme } from '../../../style'; +import { View } from '../../common/View'; +import { Checkbox } from '../../forms'; +import { Row, Cell } from '../../table'; + +type UserAccessProps = { + access: UserAvailable; + hovered?: boolean; + onHover?: (id: string | null) => void; +}; + +export const UserAccessRow = memo( + ({ access, hovered, onHover }: UserAccessProps) => { + const { t } = useTranslation(); + + const backgroundFocus = hovered; + const [marked, setMarked] = useState( + access.owner === 1 || access.haveAccess === 1, + ); + const [cloudFileId] = useMetadataPref('cloudFileId'); + const actions = useActions(); + + const handleAccessToggle = async () => { + const newValue = !marked; + if (newValue) { + const { error } = await send('access-add', { + fileId: cloudFileId as string, + userId: access.userId, + }); + + if (error) { + handleError(error); + } + } else { + const { someDeletionsFailed } = await send('access-delete-all', { + fileId: cloudFileId as string, + ids: [access.userId], + }); + + if (someDeletionsFailed) { + actions.addNotification({ + type: 'error', + title: t('Access Revocation Incomplete'), + message: t( + 'Some access permissions were not revoked successfully.', + ), + sticky: true, + }); + } + } + setMarked(newValue); + }; + + const handleError = (error: string) => { + if (error === 'token-expired') { + actions.addNotification({ + type: 'error', + id: 'login-expired', + title: t('Login expired'), + sticky: true, + message: getUserAccessErrors(error), + button: { + title: t('Go to login'), + action: () => { + actions.signOut(); + }, + }, + }); + } else { + actions.addNotification({ + type: 'error', + title: t('Something happened while editing access'), + sticky: true, + message: getUserAccessErrors(error), + }); + } + }; + + return ( + onHover && onHover(access.userId)} + onMouseLeave={() => onHover && onHover(null)} + > + + + + + + {access.displayName ?? access.userName} + + + + + + + + + ); + }, +); + +UserAccessRow.displayName = 'UserRow'; diff --git a/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx new file mode 100644 index 00000000000..f5b59d57a41 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx @@ -0,0 +1,370 @@ +// @ts-strict-ignore +import { + useState, + useEffect, + useCallback, + useMemo, + type SetStateAction, + type Dispatch, + type CSSProperties, +} from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; + +import { pushModal } from 'loot-core/src/client/actions/modals'; +import { send } from 'loot-core/src/platform/client/fetch'; +import * as undo from 'loot-core/src/platform/client/undo'; +import { + type NewUserEntity, + type UserEntity, +} from 'loot-core/types/models/user'; + +import { useActions } from '../../../hooks/useActions'; +import { SelectedProvider, useSelected } from '../../../hooks/useSelected'; +import { theme } from '../../../style'; +import { Button } from '../../common/Button2'; +import { Link } from '../../common/Link'; +import { Search } from '../../common/Search'; +import { SimpleTable } from '../../common/SimpleTable'; +import { Stack } from '../../common/Stack'; +import { Text } from '../../common/Text'; +import { View } from '../../common/View'; + +import { UserDirectoryHeader } from './UserDirectoryHeader'; +import { UserDirectoryRow } from './UserDirectoryRow'; + +type ManageUserDirectoryContentProps = { + isModal: boolean; + setLoading?: Dispatch>; +}; + +function useGetUserDirectoryErrors() { + const { t } = useTranslation(); + + function getUserDirectoryErrors(reason) { + switch (reason) { + case 'unauthorized': + return t('You are not logged in.'); + case 'token-expired': + return t('Login expired, please login again.'); + case 'user-cant-be-empty': + return t( + 'Please enter a value for the username; the field cannot be empty.', + ); + case 'role-cant-be-empty': + return t('Select a role; the field cannot be empty.'); + case 'user-already-exists': + return t( + 'The username you entered already exists. Please choose a different username.', + ); + case 'not-all-deleted': + return t( + 'Not all users were deleted. Check if one of the selected users is the server owner.', + ); + case 'role-does-not-exists': + return t( + 'Selected role does not exists, possibly a bug? Visit https://actualbudget.org/contact/ for support.', + ); + default: + return t( + 'An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: {{reason}})', + { reason }, + ); + } + } + + return { getUserDirectoryErrors }; +} + +function UserDirectoryContent({ + isModal, + setLoading, +}: ManageUserDirectoryContentProps) { + const { t } = useTranslation(); + + const [allUsers, setAllUsers] = useState([]); + const [page, setPage] = useState(0); + const [filter, setFilter] = useState(''); + const dispatch = useDispatch(); + const actions = useActions(); + + const { getUserDirectoryErrors } = useGetUserDirectoryErrors(); + + const filteredUsers = useMemo(() => { + return ( + filter === '' + ? allUsers + : allUsers.filter( + user => + user.displayName.toLowerCase().includes(filter.toLowerCase()) || + user.userName.toLowerCase().includes(filter.toLowerCase()) || + user.role.toLowerCase().includes(filter.toLowerCase()), + ) + ).slice(0, 100 + page * 50); + }, [allUsers, filter, page]); + const selectedInst = useSelected('manage-users', allUsers, []); + const [hoveredUser, setHoveredUser] = useState(null); + + const onSearchChange = useCallback( + (value: string) => { + setFilter(value); + setPage(0); + }, + [setFilter], + ); + + const loadUsers = useCallback(async () => { + setLoading(true); + + const loadedUsers = (await send('users-get')) ?? []; + + setAllUsers(loadedUsers); + setLoading(false); + return loadedUsers; + }, [setLoading]); + + useEffect(() => { + async function loadData() { + await loadUsers(); + setLoading(false); + } + + loadData(); + + return () => { + undo.setUndoState('openModal', null); + }; + }, [setLoading, loadUsers]); + + function loadMore() { + setPage(page => page + 1); + } + + const onDeleteSelected = useCallback(async () => { + setLoading(true); + const { error } = await send('user-delete-all', [...selectedInst.items]); + + if (error) { + if (error === 'token-expired') { + actions.addNotification({ + type: 'error', + id: 'login-expired', + title: t('Login expired'), + sticky: true, + message: getUserDirectoryErrors(error), + button: { + title: t('Go to login'), + action: () => actions.signOut(), + }, + }); + } else { + actions.addNotification({ + type: 'error', + title: t('Something happened while deleting users'), + sticky: true, + message: getUserDirectoryErrors(error), + }); + } + } + + await loadUsers(); + selectedInst.dispatch({ type: 'select-none' }); + setLoading(false); + }, [actions, loadUsers, selectedInst, setLoading, getUserDirectoryErrors, t]); + + const onEditUser = useCallback( + user => { + dispatch( + pushModal('edit-user', { + user, + onSave: async () => { + await loadUsers(); + setLoading(false); + }, + }), + ); + }, + [dispatch, loadUsers, setLoading], + ); + + function onAddUser() { + const user: NewUserEntity = { + userName: '', + role: null, + enabled: true, + displayName: '', + }; + + dispatch( + pushModal('edit-user', { + user, + onSave: async () => { + await loadUsers(); + setLoading(false); + }, + }), + ); + } + + const onHover = useCallback(id => { + setHoveredUser(id); + }, []); + + return ( + + + + + + + Manage and view users who can create new budgets or be invited + to access existing ones.{' '} + + Learn more + + + + + + + + + + + + {filteredUsers.length === 0 ? ( + + ) : ( + + )} + + + + + {selectedInst.items.size > 0 && ( + + )} + + + + + + ); +} + +type EmptyMessageProps = { + text: string; + style?: CSSProperties; +}; + +function EmptyMessage({ text, style }: EmptyMessageProps) { + return ( + + {text} + + ); +} + +type ManageUsersProps = { + isModal: boolean; + setLoading?: Dispatch>; +}; + +export function UserDirectory({ + isModal, + setLoading = () => {}, +}: ManageUsersProps) { + return ; +} + +type UsersListProps = { + users: UserEntity[]; + selectedItems: Set; + hoveredUser?: string; + onHover?: (id: string | null) => void; + onEditUser?: (rule: UserEntity) => void; +}; + +function UsersList({ + users, + selectedItems, + hoveredUser, + onHover, + onEditUser, +}: UsersListProps) { + if (users.length === 0) { + return null; + } + + return ( + + {users.map(user => { + const hovered = hoveredUser === user.id; + const selected = selectedItems.has(user.id); + + return ( + + ); + })} + + ); +} diff --git a/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryHeader.tsx b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryHeader.tsx new file mode 100644 index 00000000000..6cc105f6f74 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryHeader.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { + useSelectedItems, + useSelectedDispatch, +} from '../../../hooks/useSelected'; +import { SelectCell, Cell, TableHeader } from '../../table'; + +export function UserDirectoryHeader() { + const { t } = useTranslation(); + + const selectedItems = useSelectedItems(); + const dispatchSelected = useSelectedDispatch(); + + return ( + + 0} + onSelect={e => + dispatchSelected({ type: 'select-all', isRangeSelect: e.shiftKey }) + } + /> + + + + + + + + ); +} diff --git a/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryPage.tsx b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryPage.tsx new file mode 100644 index 00000000000..ec8fd223f6e --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryPage.tsx @@ -0,0 +1,49 @@ +import React, { type ReactNode } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { useNavigate } from '../../../hooks/useNavigate'; +import { Button } from '../../common/Button2'; +import { View } from '../../common/View'; +import { Page } from '../../Page'; + +import { UserDirectory } from './UserDirectory'; + +export function UserDirectoryPage({ + bottomContent, +}: { + bottomContent?: ReactNode; +}) { + const { t } = useTranslation(); + + return ( + + + + {bottomContent} + + + ); +} + +export function BackToFileListButton() { + const navigate = useNavigate(); + + return ( + + ); +} diff --git a/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryRow.tsx b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryRow.tsx new file mode 100644 index 00000000000..391b9f33912 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryRow.tsx @@ -0,0 +1,144 @@ +// @ts-strict-ignore +import React, { memo } from 'react'; +import { Trans } from 'react-i18next'; + +import { PossibleRoles, type UserEntity } from 'loot-core/types/models/user'; + +import { useSelectedDispatch } from '../../../hooks/useSelected'; +import { theme } from '../../../style'; +import { Button } from '../../common/Button2'; +import { View } from '../../common/View'; +import { Checkbox } from '../../forms'; +import { SelectCell, Row, Cell } from '../../table'; + +type UserDirectoryProps = { + user: UserEntity; + hovered?: boolean; + selected?: boolean; + onHover?: (id: string | null) => void; + onEditUser?: (user: UserEntity) => void; +}; + +export const UserDirectoryRow = memo( + ({ user, hovered, selected, onHover, onEditUser }: UserDirectoryProps) => { + const dispatchSelected = useSelectedDispatch(); + const borderColor = selected ? theme.tableBorderSelected : 'none'; + const backgroundFocus = hovered; + + return ( + onHover && onHover(user.id)} + onMouseLeave={() => onHover && onHover(null)} + > + {!user.owner && ( + { + dispatchSelected({ + type: 'select', + id: user.id, + isRangeSelect: e.shiftKey, + }); + }} + selected={selected} + /> + )} + {user.owner && ( + + )} + + + + {user.userName} + + + + + + {user.displayName} + + + + + {PossibleRoles[user.role]} + + + + + + + + + + + + + + + ); + }, +); + +UserDirectoryRow.displayName = 'UserRow'; diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx index 226f1b1c232..cd9f20a122c 100644 --- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx +++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx @@ -6,6 +6,7 @@ import { generateAccount } from 'loot-core/src/mocks'; import { TestProvider } from 'loot-core/src/mocks/redux'; import type { AccountEntity, PayeeEntity } from 'loot-core/types/models'; +import { AuthProvider } from '../../auth/AuthProvider'; import { useCommonPayees } from '../../hooks/usePayees'; import { ResponsiveProvider } from '../responsive/ResponsiveProvider'; @@ -63,17 +64,19 @@ function renderPayeeAutocomplete( render( - -
- -
-
+ + +
+ +
+
+
, ); return screen.getByTestId('autocomplete-test'); diff --git a/packages/desktop-client/src/components/common/Button.tsx b/packages/desktop-client/src/components/common/Button.tsx index d0662efe6b4..fdad12cd3bc 100644 --- a/packages/desktop-client/src/components/common/Button.tsx +++ b/packages/desktop-client/src/components/common/Button.tsx @@ -7,6 +7,8 @@ import React, { import { css } from '@emotion/css'; +import { useAuth } from '../../auth/AuthProvider'; +import { type Permissions } from '../../auth/types'; import { AnimatedLoading } from '../../icons/AnimatedLoading'; import { styles, theme } from '../../style'; @@ -25,6 +27,7 @@ type ButtonProps = HTMLProps & { textStyle?: CSSProperties; bounce?: boolean; as?: ElementType; + permission?: Permissions; }; type ButtonType = 'normal' | 'primary' | 'bare' | 'menu' | 'menuSelected'; @@ -138,10 +141,13 @@ export const Button = forwardRef( activeStyle, bounce = true, as = 'button', + permission, ...nativeProps - }, + }: ButtonProps, ref, ) => { + const { hasPermission } = useAuth(); + const typeWithDisabled: ButtonType | `${ButtonType}Disabled` = disabled ? `${type}Disabled` : type; @@ -186,7 +192,7 @@ export const Button = forwardRef( {...(typeof as === 'string' ? { className: css(buttonStyle) } : { style: buttonStyle })} - disabled={disabled} + disabled={disabled ? disabled : !hasPermission(permission)} type={isSubmit ? 'submit' : 'button'} {...nativeProps} > diff --git a/packages/desktop-client/src/components/common/Button2.tsx b/packages/desktop-client/src/components/common/Button2.tsx index 7fda685aec2..d32e6f64b61 100644 --- a/packages/desktop-client/src/components/common/Button2.tsx +++ b/packages/desktop-client/src/components/common/Button2.tsx @@ -9,6 +9,8 @@ import { Button as ReactAriaButton } from 'react-aria-components'; import { css } from '@emotion/css'; +import { useAuth } from '../../auth/AuthProvider'; +import { type Permissions } from '../../auth/types'; import { AnimatedLoading } from '../../icons/AnimatedLoading'; import { styles, theme } from '../../style'; @@ -132,13 +134,22 @@ type ButtonProps = ComponentPropsWithoutRef & { variant?: ButtonVariant; bounce?: boolean; children?: ReactNode; + permission?: Permissions; }; type ButtonVariant = 'normal' | 'primary' | 'bare' | 'menu' | 'menuSelected'; export const Button = forwardRef( (props, ref) => { - const { children, variant = 'normal', bounce = true, ...restProps } = props; + const { + permission, + children, + variant = 'normal', + bounce = true, + ...restProps + } = props; + + const { hasPermission } = useAuth(); const variantWithDisabled: ButtonVariant | `${ButtonVariant}Disabled` = props.isDisabled ? `${variant}Disabled` : variant; @@ -173,6 +184,7 @@ export const Button = forwardRef( return ( void; diff --git a/packages/desktop-client/src/components/manager/BudgetList.tsx b/packages/desktop-client/src/components/manager/BudgetList.tsx index 12defd2e4dd..03b8ab1300e 100644 --- a/packages/desktop-client/src/components/manager/BudgetList.tsx +++ b/packages/desktop-client/src/components/manager/BudgetList.tsx @@ -1,4 +1,10 @@ -import React, { useState, useRef, type CSSProperties } from 'react'; +import React, { + useState, + useRef, + useEffect, + type CSSProperties, + useCallback, +} from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; @@ -17,6 +23,7 @@ import { isNonProductionEnvironment, } from 'loot-core/src/shared/environment'; import { + type RemoteFile, type File, type LocalFile, type SyncableLocalFile, @@ -32,6 +39,8 @@ import { SvgCog, SvgDotsHorizontalTriple, SvgFileDouble, + SvgUser, + SvgUserGroup, } from '../../icons/v1'; import { SvgCloudUnknown, SvgKey, SvgRefreshArrow } from '../../icons/v2'; import { styles, theme } from '../../style'; @@ -40,8 +49,10 @@ import { Button } from '../common/Button2'; import { Menu } from '../common/Menu'; import { Popover } from '../common/Popover'; import { Text } from '../common/Text'; +import { Tooltip } from '../common/Tooltip'; import { View } from '../common/View'; import { useResponsive } from '../responsive/ResponsiveProvider'; +import { useMultiuserEnabled } from '../ServerContext'; function getFileDescription(file: File, t: (key: string) => string) { if (file.state === 'unknown') { @@ -61,6 +72,10 @@ function getFileDescription(file: File, t: (key: string) => string) { return null; } +function isLocalFile(file: File): file is LocalFile { + return file.state === 'local'; +} + function FileMenu({ onDelete, onClose, @@ -132,52 +147,112 @@ function FileMenuButton({ ); } -function FileState({ file }: { file: File }) { +function FileState({ + file, + currentUserId, +}: { + file: File; + currentUserId: string; +}) { const { t } = useTranslation(); + const multiuserEnabled = useMultiuserEnabled(); let Icon; let status; let color; + let ownerName = null; + + const getOwnerDisplayName = useCallback(() => { + if ('usersWithAccess' in file) { + const userFound = file.usersWithAccess?.find(f => f.owner); + + if (userFound?.userName === '') { + return 'Server'; + } + + return userFound?.displayName ?? userFound?.userName ?? 'Unassigned'; + } + + return 'Unknown'; + }, [file]); switch (file.state) { case 'unknown': Icon = SvgCloudUnknown; status = t('Network unavailable'); color = theme.buttonNormalDisabledText; + ownerName = 'Unknown'; break; case 'remote': Icon = SvgCloudDownload; status = t('Available for download'); + ownerName = getOwnerDisplayName(); break; case 'local': + Icon = SvgFileDouble; + status = 'Local'; + ownerName = 'You'; + break; case 'broken': + ownerName = 'unknown'; Icon = SvgFileDouble; status = t('Local'); + ownerName = 'You'; break; default: Icon = SvgCloudCheck; status = t('Syncing'); + ownerName = getOwnerDisplayName(); break; } + const showOwnerContent = multiuserEnabled && file.owner !== currentUserId; + return ( - - + + > + - {status} + {status} + + + + {showOwnerContent && ( + + + Owner: + + + {ownerName} + + + )} + ); } @@ -188,14 +263,17 @@ function FileItem({ onSelect, onDelete, onDuplicate, + currentUserId, }: { file: File; quickSwitchMode: boolean; onSelect: (file: File) => void; onDelete: (file: File) => void; onDuplicate: (file: File) => void; + currentUserId: string; }) { const { t } = useTranslation(); + const multiuserEnabled = useMultiuserEnabled(); const selecting = useRef(false); @@ -231,11 +309,19 @@ function FileItem({ > - {file.name} - - + + {file.name} + {multiuserEnabled && 'cloudFileId' in file && ( + + )} + + + void; onDelete: (file: File) => void; onDuplicate: (file: File) => void; + currentUserId: string; }) { - function isLocalFile(file: File): file is LocalFile { - return file.state === 'local'; - } - return ( state.budgets.allFiles || []); + const multiuserEnabled = useMultiuserEnabled(); const [id] = useMetadataPref('id'); + const [currentUserId, setCurrentUserId] = useState(''); + const userData = useSelector(state => state.user.data); + + const fetchUsers = useCallback(async () => { + try { + setCurrentUserId(userData?.userId ?? ''); + } catch (error) { + console.error('Failed to fetch users:', error); + } + }, [userData?.userId]); + + useEffect(() => { + if (multiuserEnabled && !userData?.offline) { + fetchUsers(); + } + }, [multiuserEnabled, userData?.offline, fetchUsers]); // Remote files do not have the 'id' field function isNonRemoteFile( @@ -423,6 +525,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { ): file is LocalFile | SyncableLocalFile | SyncedLocalFile { return file.state !== 'remote'; } + const nonRemoteFiles = allFiles.filter(isNonRemoteFile); const files = id ? nonRemoteFiles.filter(f => f.id !== id) : allFiles; @@ -470,6 +573,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { return ( @@ -558,3 +663,104 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { ); } + +type UserAccessForFileProps = { + fileId: string; + currentUserId: string; +}; + +function UserAccessForFile({ fileId, currentUserId }: UserAccessForFileProps) { + const allFiles = useSelector(state => state.budgets.allFiles || []); + const remoteFiles = allFiles.filter( + f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', + ) as (SyncedLocalFile | RemoteFile)[]; + const currentFile = remoteFiles.find(f => f.cloudFileId === fileId); + const multiuserEnabled = useMultiuserEnabled(); + + let usersAccess = currentFile?.usersWithAccess ?? []; + usersAccess = usersAccess?.filter(user => user.userName !== '') ?? []; + + const sortedUsersAccess = [...usersAccess].sort((a, b) => { + const textA = + a.userId === currentUserId ? 'You' : (a.displayName ?? a.userName); + const textB = + b.userId === currentUserId ? 'You' : (b.displayName ?? b.userName); + return textA.localeCompare(textB); + }); + + return ( + + {multiuserEnabled && + usersAccess.length > 0 && + !(sortedUsersAccess.length === 1 && sortedUsersAccess[0].owner) && ( + + + + File shared with: + + + {sortedUsersAccess.map(user => ( + + + + {user.userId === currentUserId + ? 'You' + : (user.displayName ?? user.userName)} + + + ))} + + + } + placement="bottom end" + > + + + + )} + + ); +} diff --git a/packages/desktop-client/src/components/manager/ManagementApp.tsx b/packages/desktop-client/src/components/manager/ManagementApp.tsx index 194ecadd1ba..05f22b26dd9 100644 --- a/packages/desktop-client/src/components/manager/ManagementApp.tsx +++ b/packages/desktop-client/src/components/manager/ManagementApp.tsx @@ -4,16 +4,22 @@ import { Navigate, Route, Routes } from 'react-router-dom'; import { loggedIn, setAppState } from 'loot-core/client/actions'; +import { ProtectedRoute } from '../../auth/ProtectedRoute'; +import { Permissions } from '../../auth/types'; import { useMetaThemeColor } from '../../hooks/useMetaThemeColor'; import { theme } from '../../style'; import { tokens } from '../../tokens'; +import { + BackToFileListButton, + UserDirectoryPage, +} from '../admin/UserDirectory/UserDirectoryPage'; import { AppBackground } from '../AppBackground'; import { Text } from '../common/Text'; import { View } from '../common/View'; import { LoggedInUser } from '../LoggedInUser'; import { Notifications } from '../Notifications'; import { useResponsive } from '../responsive/ResponsiveProvider'; -import { useServerVersion } from '../ServerContext'; +import { useMultiuserEnabled, useServerVersion } from '../ServerContext'; import { BudgetList } from './BudgetList'; import { ConfigServer } from './ConfigServer'; @@ -22,6 +28,7 @@ import { Bootstrap } from './subscribe/Bootstrap'; import { ChangePassword } from './subscribe/ChangePassword'; import { Error } from './subscribe/Error'; import { Login } from './subscribe/Login'; +import { OpenIdCallback } from './subscribe/OpenIdCallback'; import { WelcomeScreen } from './WelcomeScreen'; function Version() { @@ -58,6 +65,8 @@ export function ManagementApp() { const files = useSelector(state => state.budgets.allFiles); const isLoading = useSelector(state => state.app.loadingText !== null); const userData = useSelector(state => state.user.data); + const multiuserEnabled = useMultiuserEnabled(); + const managerHasInitialized = useSelector( state => state.app.managerHasInitialized, ); @@ -127,6 +136,22 @@ export function ManagementApp() { ) : ( } /> )} + + {multiuserEnabled && ( + } + /> + } + /> + } + /> + )} {/* Redirect all other pages to this route */} } /> @@ -156,10 +181,23 @@ export function ManagementApp() { ) : ( - } /> + } /> + } /> } /> } /> } /> + {multiuserEnabled && ( + } + /> + } + /> + )} + {/* Redirect all other pages to this route */} } /> diff --git a/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx b/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx index 863c469c1aa..3e2d48b199d 100644 --- a/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx @@ -4,15 +4,16 @@ import { Trans, useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { createBudget } from 'loot-core/src/client/actions/budgets'; -import { loggedIn } from 'loot-core/src/client/actions/user'; import { send } from 'loot-core/src/platform/client/fetch'; +import { useNavigate } from '../../../hooks/useNavigate'; import { theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Link } from '../../common/Link'; import { Paragraph } from '../../common/Paragraph'; import { Text } from '../../common/Text'; import { View } from '../../common/View'; +import { useRefreshLoginMethods } from '../../ServerContext'; import { useBootstrapped, Title } from './common'; import { ConfirmPasswordForm } from './ConfirmPasswordForm'; @@ -21,8 +22,10 @@ export function Bootstrap() { const { t } = useTranslation(); const dispatch = useDispatch(); const [error, setError] = useState(null); + const refreshLoginMethods = useRefreshLoginMethods(); const { checked } = useBootstrapped(); + const navigate = useNavigate(); function getErrorMessage(error) { switch (error) { @@ -32,6 +35,12 @@ export function Bootstrap() { return t('Passwords do not match'); case 'network-failure': return t('Unable to contact the server'); + case 'missing-issuer': + return t('OpenID server cannot be empty'); + case 'missing-client-id': + return t('Client ID cannot be empty'); + case 'missing-client-secret': + return t('Client secret cannot be empty'); default: return t(`An unknown error occurred: {{error}}`, { error }); } @@ -44,7 +53,8 @@ export function Bootstrap() { if (error) { setError(error); } else { - dispatch(loggedIn()); + await refreshLoginMethods(); + navigate('/login'); } } @@ -57,7 +67,7 @@ export function Bootstrap() { } return ( - + <Paragraph style={{ fontSize: 16, color: theme.pageTextDark }}> <Trans> @@ -94,7 +104,11 @@ export function Bootstrap() { buttons={ <Button variant="bare" - style={{ fontSize: 15, color: theme.pageTextLink, marginRight: 15 }} + style={{ + fontSize: 15, + color: theme.pageTextLink, + marginRight: 15, + }} onPress={onDemo} > {t('Try Demo')} diff --git a/packages/desktop-client/src/components/manager/subscribe/ConfirmPasswordForm.tsx b/packages/desktop-client/src/components/manager/subscribe/ConfirmPasswordForm.tsx index ae6c4db017e..cfb64077e44 100644 --- a/packages/desktop-client/src/components/manager/subscribe/ConfirmPasswordForm.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/ConfirmPasswordForm.tsx @@ -1,12 +1,22 @@ // @ts-strict-ignore -import React, { useState } from 'react'; +import React, { type ChangeEvent, type ReactNode, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; +import { theme } from '../../../style'; import { ButtonWithLoading } from '../../common/Button2'; import { BigInput } from '../../common/Input'; import { View } from '../../common/View'; -export function ConfirmPasswordForm({ buttons, onSetPassword, onError }) { +type ConfirmPasswordFormProps = { + buttons: ReactNode; + onSetPassword: (password: string) => Promise<void>; + onError: (error: string) => void; +}; +export function ConfirmPasswordForm({ + buttons, + onSetPassword, + onError, +}: ConfirmPasswordFormProps) { const { t } = useTranslation(); const [password1, setPassword1] = useState(''); @@ -83,3 +93,75 @@ export function ConfirmPasswordForm({ buttons, onSetPassword, onError }) { </View> ); } + +export function ConfirmOldPasswordForm({ buttons, onSetPassword }) { + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + const { t } = useTranslation(); + + async function onSubmit() { + if (loading) { + return; + } + + setLoading(true); + await onSetPassword(password); + setLoading(false); + } + + function onShowPassword(e) { + setShowPassword(e.target.checked); + } + + return ( + <View + style={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + marginTop: 30, + }} + > + <BigInput + autoFocus={true} + placeholder={t('Password')} + type={showPassword ? 'text' : 'password'} + value={password} + onChange={(e: ChangeEvent<HTMLInputElement>) => + setPassword(e.target.value) + } + onEnter={onSubmit} + style={{ + borderColor: theme.buttonMenuBorder, + borderWidth: 1, + borderStyle: 'solid', + ':focus': {}, + }} + /> + + <View + style={{ + flexDirection: 'row', + alignItems: 'center', + fontSize: 15, + marginTop: 20, + }} + > + <label style={{ userSelect: 'none' }}> + <input type="checkbox" onChange={onShowPassword} />{' '} + <Trans>Show password</Trans> + </label> + <View style={{ flex: 1 }} /> + {buttons} + <ButtonWithLoading + variant="primary" + isLoading={loading} + onPress={onSubmit} + > + <Trans>OK</Trans> + </ButtonWithLoading> + </View> + </View> + ); +} diff --git a/packages/desktop-client/src/components/manager/subscribe/Login.tsx b/packages/desktop-client/src/components/manager/subscribe/Login.tsx index bbc2c4f99b1..df8aefaf423 100644 --- a/packages/desktop-client/src/components/manager/subscribe/Login.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/Login.tsx @@ -2,44 +2,223 @@ import React, { useState, useEffect } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; -import { useParams, useSearchParams } from 'react-router-dom'; +import { useSearchParams } from 'react-router-dom'; -import { createBudget } from 'loot-core/src/client/actions/budgets'; +import { isElectron } from 'loot-core/shared/environment'; import { loggedIn } from 'loot-core/src/client/actions/user'; import { send } from 'loot-core/src/platform/client/fetch'; +import { type OpenIdConfig } from 'loot-core/types/models/openid'; +import { useNavigate } from '../../../hooks/useNavigate'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; -import { theme } from '../../../style'; +import { styles, theme } from '../../../style'; import { Button, ButtonWithLoading } from '../../common/Button2'; import { BigInput } from '../../common/Input'; +import { Label } from '../../common/Label'; import { Link } from '../../common/Link'; +import { Select } from '../../common/Select'; import { Text } from '../../common/Text'; import { View } from '../../common/View'; +import { useAvailableLoginMethods, useLoginMethod } from '../../ServerContext'; import { useBootstrapped, Title } from './common'; +import { OpenIdForm } from './OpenIdForm'; + +function PasswordLogin({ setError, dispatch }) { + const [password, setPassword] = useState(''); + const [loading, setLoading] = useState(false); + const { t } = useTranslation(); + + async function onSubmitPassword() { + if (password === '' || loading) { + return; + } + + setError(null); + setLoading(true); + const { error } = await send('subscribe-sign-in', { + password, + loginMethod: 'password', + }); + setLoading(false); + + if (error) { + setError(error); + } else { + dispatch(loggedIn()); + } + } + + return ( + <View style={{ flexDirection: 'row', marginTop: 5 }}> + <BigInput + autoFocus={true} + placeholder={t('Password')} + type="password" + onChangeValue={newValue => setPassword(newValue)} + style={{ flex: 1, marginRight: 10 }} + onEnter={onSubmitPassword} + /> + <ButtonWithLoading + variant="primary" + isLoading={loading} + style={{ fontSize: 15, width: 170 }} + onPress={onSubmitPassword} + > + <Trans>Sign in</Trans> + </ButtonWithLoading> + </View> + ); +} + +function OpenIdLogin({ setError }) { + const [warnMasterCreation, setWarnMasterCreation] = useState(false); + const [reviewOpenIdConfiguration, setReviewOpenIdConfiguration] = + useState(false); + const navigate = useNavigate(); + + async function onSetOpenId(config: OpenIdConfig) { + setError(null); + const { error } = await send('subscribe-bootstrap', { openId: config }); + + if (error) { + setError(error); + } else { + navigate('/'); + } + } + + useEffect(() => { + send('owner-created').then(created => setWarnMasterCreation(!created)); + }, []); + + async function onSubmitOpenId() { + const { error, redirect_url } = await send('subscribe-sign-in', { + return_url: isElectron() + ? await window.Actual.startOAuthServer() + : window.location.origin, + loginMethod: 'openid', + }); + + if (error) { + setError(error); + } else { + if (isElectron()) { + window.Actual?.openURLInBrowser(redirect_url); + } else { + window.location.href = redirect_url; + } + } + } + + return ( + <View> + {!reviewOpenIdConfiguration && ( + <> + <View style={{ flexDirection: 'row', justifyContent: 'flex-end' }}> + <Button + variant="primary" + style={{ + padding: 10, + fontSize: 14, + width: 170, + marginTop: 5, + }} + onPress={onSubmitOpenId} + > + <Trans>Sign in with OpenID</Trans> + </Button> + </View> + {warnMasterCreation && ( + <> + <label style={{ color: theme.warningText, marginTop: 10 }}> + <Trans> + The first user to login with OpenID will be the{' '} + <Text style={{ fontWeight: 'bold' }}>server owner</Text>. This + can't be changed using UI. + </Trans> + </label> + <Button + variant="bare" + onPress={() => setReviewOpenIdConfiguration(true)} + style={{ marginTop: 5 }} + > + <Trans>Review OpenID configuration</Trans> + </Button> + </> + )} + </> + )} + {reviewOpenIdConfiguration && ( + <OpenIdForm + loadData={true} + otherButtons={[ + <Button + key="cancel" + variant="bare" + style={{ marginRight: 10 }} + onPress={() => setReviewOpenIdConfiguration(false)} + > + <Trans>Cancel</Trans> + </Button>, + ]} + onSetOpenId={async config => { + onSetOpenId(config); + }} + /> + )} + </View> + ); +} + +function HeaderLogin({ error }) { + return ( + <View + style={{ + flexDirection: 'row', + justifyContent: 'center', + marginTop: 15, + }} + > + {error ? ( + <Link + variant="button" + type="button" + style={{ fontSize: 15 }} + to={'/login/password?error=' + error} + > + <Trans>Login with Password</Trans> + </Link> + ) : ( + <span> + <Trans>Checking Header Token Login ...</Trans>{' '} + <AnimatedLoading style={{ width: 20, height: 20 }} /> + </span> + )} + </View> + ); +} export function Login() { const { t } = useTranslation(); const dispatch = useDispatch(); - const { method = 'password' } = useParams(); + const defaultLoginMethod = useLoginMethod(); + const [method, setMethod] = useState(defaultLoginMethod); const [searchParams, _setSearchParams] = useSearchParams(); - const [password, setPassword] = useState(''); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(searchParams.get('error')); - const { checked } = useBootstrapped(!searchParams.has('error')); + const [error, setError] = useState(null); + const { checked } = useBootstrapped(); + const loginMethods = useAvailableLoginMethods(); useEffect(() => { if (checked && !searchParams.has('error')) { (async () => { if (method === 'header') { setError(null); - setLoading(true); const { error } = await send('subscribe-sign-in', { password: '', loginMethod: method, }); - setLoading(false); if (error) { setError(error); @@ -49,47 +228,25 @@ export function Login() { } })(); } - }, [checked, searchParams, method, dispatch]); + }, [loginMethods, checked, searchParams, method, dispatch]); function getErrorMessage(error) { switch (error) { case 'invalid-header': - return 'Auto login failed - No header sent'; + return t('Auto login failed - No header sent'); case 'proxy-not-trusted': - return 'Auto login failed - Proxy not trusted'; + return t('Auto login failed - Proxy not trusted'); case 'invalid-password': - return 'Invalid password'; + return t('Invalid password'); case 'network-failure': - return 'Unable to contact the server'; + return t('Unable to contact the server'); + case 'internal-error': + return t('Internal error'); default: - return `An unknown error occurred: ${error}`; + return t(`An unknown error occurred: {{error}}`, { error }); } } - async function onSubmit() { - if (password === '' || loading) { - return; - } - - setError(null); - setLoading(true); - const { error } = await send('subscribe-sign-in', { - password, - loginMethod: method, - }); - setLoading(false); - - if (error) { - setError(error); - } else { - dispatch(loggedIn()); - } - } - - async function onDemo() { - await dispatch(createBudget({ demoMode: true })); - } - if (!checked) { return null; } @@ -97,18 +254,43 @@ export function Login() { return ( <View style={{ maxWidth: 450, marginTop: -30, color: theme.pageText }}> <Title text={t('Sign in to this Actual instance')} /> - <Text - style={{ - fontSize: 16, - color: theme.pageTextDark, - lineHeight: 1.4, - }} - > - <Trans> - If you lost your password, you likely still have access to your server - to manually reset it. - </Trans> - </Text> + + {loginMethods?.length > 1 && ( + <Text + style={{ + fontSize: 16, + color: theme.pageTextDark, + lineHeight: 1.4, + marginBottom: 10, + }} + > + <Trans> + If you lost your password, you likely still have access to your + server to manually reset it. + </Trans> + </Text> + )} + + {loginMethods?.length > 1 && ( + <View style={{ marginTop: 10 }}> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + paddingTop: 5, + }} + title={t('Select the login method')} + /> + <Select + value={method} + onChange={newValue => { + setError(null); + setMethod(newValue); + }} + options={loginMethods?.map(m => [m.method, m.displayName])} + /> + </View> + )} {error && ( <Text @@ -124,66 +306,12 @@ export function Login() { )} {method === 'password' && ( - <View style={{ display: 'flex', flexDirection: 'row', marginTop: 30 }}> - <BigInput - autoFocus={true} - placeholder={t('Password')} - type="password" - onChangeValue={setPassword} - style={{ flex: 1, marginRight: 10 }} - onEnter={onSubmit} - /> - <ButtonWithLoading - variant="primary" - isLoading={loading} - style={{ fontSize: 15 }} - onPress={onSubmit} - > - <Trans>Sign in</Trans> - </ButtonWithLoading> - </View> - )} - {method === 'header' && ( - <View - style={{ - flexDirection: 'row', - justifyContent: 'center', - marginTop: 15, - }} - > - {error && ( - <Link - variant="button" - buttonVariant="primary" - style={{ fontSize: 15 }} - to={'/login/password?error=' + error} - > - <Trans>Login with Password</Trans> - </Link> - )} - {!error && ( - <span> - <Trans>Checking Header Token Login ...</Trans>{' '} - <AnimatedLoading style={{ width: 20, height: 20 }} /> - </span> - )} - </View> + <PasswordLogin setError={setError} dispatch={dispatch} /> )} - <View - style={{ - flexDirection: 'row', - justifyContent: 'center', - marginTop: 15, - }} - > - <Button - variant="bare" - style={{ fontSize: 15, color: theme.pageTextLink, marginLeft: 10 }} - onPress={onDemo} - > - <Trans>Try Demo →</Trans> - </Button> - </View> + + {method === 'openid' && <OpenIdLogin setError={setError} />} + + {method === 'header' && <HeaderLogin error={error} />} </View> ); } diff --git a/packages/desktop-client/src/components/manager/subscribe/OpenIdCallback.ts b/packages/desktop-client/src/components/manager/subscribe/OpenIdCallback.ts new file mode 100644 index 00000000000..02928047eab --- /dev/null +++ b/packages/desktop-client/src/components/manager/subscribe/OpenIdCallback.ts @@ -0,0 +1,16 @@ +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; + +import { loggedIn } from 'loot-core/src/client/actions/user'; +import { send } from 'loot-core/src/platform/client/fetch'; + +export function OpenIdCallback() { + const dispatch = useDispatch(); + useEffect(() => { + const token = new URLSearchParams(window.location.search).get('token'); + send('subscribe-set-token', { token: token as string }).then(() => { + dispatch(loggedIn()); + }); + }); + return null; +} diff --git a/packages/desktop-client/src/components/manager/subscribe/OpenIdForm.tsx b/packages/desktop-client/src/components/manager/subscribe/OpenIdForm.tsx new file mode 100644 index 00000000000..0355a897329 --- /dev/null +++ b/packages/desktop-client/src/components/manager/subscribe/OpenIdForm.tsx @@ -0,0 +1,448 @@ +import { type ReactNode, useEffect, useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useLocation, type Location } from 'react-router-dom'; + +import { addNotification } from 'loot-core/client/actions'; +import { send } from 'loot-core/platform/client/fetch'; +import { type Handlers } from 'loot-core/types/handlers'; +import { type OpenIdConfig } from 'loot-core/types/models/openid'; + +import { theme, styles } from '../../../style'; +import { ButtonWithLoading } from '../../common/Button2'; +import { Input } from '../../common/Input'; +import { Link } from '../../common/Link'; +import { Menu } from '../../common/Menu'; +import { Select } from '../../common/Select'; +import { Stack } from '../../common/Stack'; +import { Text } from '../../common/Text'; +import { View } from '../../common/View'; +import { FormField, FormLabel } from '../../forms'; +import { useServerURL } from '../../ServerContext'; + +type OpenIdCallback = (config: OpenIdConfig) => Promise<void>; + +type OnProviderChangeCallback = (provider: OpenIdProviderOption) => void; + +type OpenIdFormProps = { + onSetOpenId: OpenIdCallback; + otherButtons?: ReactNode[]; + loadData?: boolean; +}; + +type OpenIdProviderOption = { + label: string; + value: string; + issuer?: string | ((location: Location, serverUrl: string) => string); + clientId?: string | ((location: Location, serverUrl: string) => string); + clientSecret?: string | ((location: Location, serverUrl: string) => string); + clientIdRequired: boolean; + clientIdDisabled?: boolean; + clientSecretRequired: boolean; + clientSecretDisabled: boolean; + submitButtonDisabled?: boolean; + tip: ReactNode; +}; + +export function OpenIdForm({ + onSetOpenId, + otherButtons, + loadData, +}: OpenIdFormProps) { + const { t } = useTranslation(); + + const [issuer, setIssuer] = useState(''); + const [clientId, setClientId] = useState(''); + const [clientSecret, setClientSecret] = useState(''); + const [clientIdRequired, setClientIdRequired] = useState(true); + const [clientIdDisabled, setClientIdDisabled] = useState(false); + const [clientSecretRequired, setClientSecretRequired] = useState(true); + const [clientSecretDisabled, setClientSecretDisabled] = useState(false); + const [providerName, setProviderName] = useState('other'); + const serverUrl = useServerURL(); + const location = useLocation(); + const [tip, setTip] = useState((<Text />) as ReactNode); + const [submitButtonDisabled, setSubmitButtonDisabled] = useState(false); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (loadData) { + send('get-openid-config').then( + (config: Awaited<ReturnType<Handlers['get-openid-config']>>) => { + if (!config) return; + + if ('error' in config) { + addNotification({ + type: 'error', + id: 'error', + title: t('Error getting openid config'), + sticky: true, + message: config.error, + }); + } else if ('openId' in config) { + setProviderName(config?.openId?.selectedProvider ?? 'other'); + setIssuer(config?.openId?.issuer ?? ''); + setClientId(config?.openId?.client_id ?? ''); + setClientSecret(config?.openId?.client_secret ?? ''); + } + }, + ); + } + }, [loadData, t]); + + const handleProviderChange = (provider: OpenIdProviderOption) => { + if (provider) { + setProviderName(provider.value); + const newIssuer = + typeof provider.issuer === 'function' + ? provider.issuer(location, serverUrl ?? '') + : provider.issuer; + + setIssuer(newIssuer ?? ''); + + const newClientId = + typeof provider.clientId === 'function' + ? provider.clientId(location, serverUrl ?? '') + : provider.clientId; + + setClientId(newClientId ?? ''); + + const newclientSecret = + typeof provider.clientSecret === 'function' + ? provider.clientSecret(location, serverUrl ?? '') + : provider.clientSecret; + + setClientSecret(newclientSecret ?? ''); + + setClientIdRequired(provider.clientIdRequired ?? true); + setClientIdDisabled(provider.clientIdDisabled ?? false); + setClientSecretRequired(provider.clientSecretRequired ?? true); + setClientSecretDisabled(provider.clientSecretDisabled ?? false); + + setTip(provider.tip ?? <Text />); + + setSubmitButtonDisabled(provider.submitButtonDisabled ?? false); + } + }; + + async function onSubmit() { + if (loading) { + return; + } + + setLoading(true); + await onSetOpenId({ + selectedProvider: providerName, + issuer: issuer ?? '', + client_id: clientId ?? '', + client_secret: clientSecret ?? '', + server_hostname: serverUrl ?? '', + }); + setLoading(false); + } + + return ( + <> + <OpenIdProviderSelector + onProviderChange={handleProviderChange} + defaultValue={providerName} + /> + <Stack direction="column" style={{ marginTop: 5 }}> + <FormField style={{ flex: 1 }}> + {!submitButtonDisabled && ( + <View> + <Input + id="issuer-field" + type="text" + value={issuer} + placeholder="https://accounts.domain.tld/" + onChangeValue={newValue => setIssuer(newValue)} + /> + </View> + )} + </FormField> + </Stack> + <label + htmlFor="issuer-field" + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + minWidth: '150px', + marginTop: 5, + marginBottom: 10, + maxWidth: '500px', + }} + > + {!submitButtonDisabled && t('The OpenID provider URL.')}{' '} + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + {tip} + </Text> + </label>{' '} + <Stack> + <FormField style={{ flex: 1 }}> + <FormLabel title={t('Client ID')} htmlFor="clientid-field" /> + <Input + type="text" + id="clientid-field" + value={clientId} + disabled={clientIdDisabled} + onChangeValue={newValue => setClientId(newValue)} + required={clientIdRequired} + /> + <label + htmlFor="clientid-field" + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans>The Client ID generated by the OpenID provider.</Trans> + </label> + </FormField> + <FormField style={{ flex: 1 }}> + <FormLabel title={t('Client secret')} htmlFor="clientsecret-field" /> + <Input + type="text" + id="clientsecret-field" + value={clientSecret} + onChangeValue={newValue => setClientSecret(newValue)} + disabled={clientSecretDisabled} + required={clientSecretRequired} + /> + <label + htmlFor="clientsecret-field" + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + The client secret associated with the ID generated by the OpenID + provider. + </Trans> + </label> + </FormField> + + <Stack direction="row" justify="flex-end" align="center"> + {otherButtons} + <ButtonWithLoading + variant="primary" + isLoading={loading} + onPress={onSubmit} + isDisabled={submitButtonDisabled} + > + OK + </ButtonWithLoading> + </Stack> + </Stack> + </> + ); +} + +const openIdProviders: (OpenIdProviderOption | typeof Menu.line)[] = [ + ...[ + { + label: 'Google Accounts', + value: 'google', + issuer: 'https://accounts.google.com', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Link + variant="external" + to="https://developers.google.com/identity/sign-in/web/sign-in" + > + <Trans>Integrating Google Sign-In into your web app</Trans> + </Link> + ), + }, + { + label: 'Passwordless.id', + value: 'passwordless', + issuer: 'https://api.passwordless.id', + clientId: (location: Location, serverUrl: string) => + serverUrl + ? serverUrl + : window.location.href.replace(location.pathname, ''), + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: true, + tip: ( + <Link variant="external" to="https://passwordless.id/"> + <Trans>Get started with passwordless.id</Trans> + </Link> + ), + }, + { + label: 'Microsoft Entra', + value: 'microsoft', + issuer: 'https://login.microsoftonline.com/{tenant-id}', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Link + variant="external" + to="https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc" + > + <Trans>OpenID Connect on the Microsoft identity platform</Trans> + </Link> + ), + }, + { + label: 'Auth0', + value: 'auth0', + issuer: 'https://{domain.region}.auth0.com/', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Text style={{ color: theme.warningText }}> + <Trans> + Note that the URL depends on your application domain and region. + </Trans>{' '} + <Link + variant="external" + to="https://auth0.com/docs/get-started/applications/application-settings" + > + <Trans>Auth0 application settings</Trans> + </Link> + </Text> + ), + }, + { + label: 'Keycloak', + value: 'keycloak', + issuer: 'https://{domain}/realms/{realm}/', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Text style={{ color: theme.warningText }}> + <Trans> + Note that the URL depends on your Keycloak domain and realm. + </Trans>{' '} + <Link + variant="external" + to="https://www.keycloak.org/docs/22.0.0/securing_apps/" + > + <Trans>Securing Applications with Keycloak</Trans> + </Link> + </Text> + ), + }, + { + label: 'Github', + value: 'github', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: true, + clientIdDisabled: true, + submitButtonDisabled: true, + tip: ( + <> + <Text style={{ color: theme.errorText }}> + <Trans> + Github does not support discovery. You need to configure it in the + server. + </Trans> + </Text>{' '} + <Link + variant="external" + to="https://actualbudget.org/docs/" + linkColor="muted" + > + <Trans>Learn more</Trans> + </Link> + </> + ), + }, + { + label: 'Authentik', + value: 'authentik', + issuer: 'https://{domain}/application/o/{provider-slug-name}/', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Text style={{ color: theme.warningText }}> + <Trans> + Note that the URL depends on your Authentik domain and provider slug + name. + </Trans>{' '} + <Link + variant="external" + to="https://docs.goauthentik.io/docs/providers/oauth2/" + > + <Trans>Configure OAuth2 Provider</Trans> + </Link> + </Text> + ), + }, + ].sort((a, b) => a.label.localeCompare(b.label)), + Menu.line, + { + label: 'Other', + value: 'other', + issuer: '', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Text> + <Trans> + Use any OpenId provider of your preference.{' '} + <Text style={{ color: theme.warningText }}> + If your provider does not support discovery, configure it manually + from server + </Text> + </Trans>{' '} + <Link + variant="external" + to="https://actualbudget.org/docs/" + linkColor="muted" + > + <Trans>Learn more</Trans> + </Link> + </Text> + ), + }, +]; + +function OpenIdProviderSelector({ + onProviderChange, + defaultValue, +}: { + onProviderChange: OnProviderChangeCallback; + defaultValue: string; +}) { + const { t } = useTranslation(); + + const handleProviderChange = (newValue: string) => { + const selectedProvider = openIdProviders.find(provider => + provider !== Menu.line ? provider.value === newValue : false, + ); + if (selectedProvider && selectedProvider !== Menu.line) { + onProviderChange(selectedProvider); + } + }; + + return ( + <FormField style={{ flex: 1, marginTop: 20 }}> + <FormLabel title={t('OpenID Provider')} htmlFor="provider-selector" /> + <Select + options={openIdProviders.map(provider => + provider === Menu.line ? Menu.line : [provider.value, provider.label], + )} + defaultLabel={t('Select Provider')} + value={defaultValue} + onChange={handleProviderChange} + /> + </FormField> + ); +} diff --git a/packages/desktop-client/src/components/manager/subscribe/common.tsx b/packages/desktop-client/src/components/manager/subscribe/common.tsx index 929b4bbbe20..be7729f7eb1 100644 --- a/packages/desktop-client/src/components/manager/subscribe/common.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/common.tsx @@ -3,10 +3,15 @@ import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { send } from 'loot-core/src/platform/client/fetch'; +import { type Handlers } from 'loot-core/types/handlers'; import { useNavigate } from '../../../hooks/useNavigate'; import { theme } from '../../../style'; -import { useSetServerURL } from '../../ServerContext'; +import { + useSetLoginMethods, + useSetMultiuserEnabled, + useSetServerURL, +} from '../../ServerContext'; // There are two URLs that dance with each other: `/login` and // `/bootstrap`. Both of these URLs check the state of the the server @@ -22,6 +27,8 @@ export function useBootstrapped(redirect = true) { const navigate = useNavigate(); const location = useLocation(); const setServerURL = useSetServerURL(); + const setMultiuserEnabled = useSetMultiuserEnabled(); + const setLoginMethods = useSetLoginMethods(); useEffect(() => { async function run() { @@ -40,7 +47,9 @@ export function useBootstrapped(redirect = true) { if (url == null && !bootstrapped) { // A server hasn't been specified yet const serverURL = window.location.origin; - const result = await send('subscribe-needs-bootstrap', { + const result: Awaited< + ReturnType<Handlers['subscribe-needs-bootstrap']> + > = await send('subscribe-needs-bootstrap', { url: serverURL, }); @@ -52,17 +61,28 @@ export function useBootstrapped(redirect = true) { await setServerURL(serverURL, { validate: false }); + setMultiuserEnabled(result.multiuser); + setLoginMethods(result.availableLoginMethods); + if (result.bootstrapped) { - ensure(`/login/${result.loginMethod}`); + ensure(`/login`); } else { ensure('/bootstrap'); } } else { - const result = await send('subscribe-needs-bootstrap'); + const result: Awaited< + ReturnType<Handlers['subscribe-needs-bootstrap']> + > = await send('subscribe-needs-bootstrap'); + if ('error' in result) { navigate('/error', { state: { error: result.error } }); } else if (result.bootstrapped) { - ensure(`/login/${result.loginMethod}`); + ensure(`/login`); + + if ('hasServer' in result && result.hasServer) { + setMultiuserEnabled(result.multiuser); + setLoginMethods(result.availableLoginMethods); + } } else { ensure('/bootstrap'); } diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx index bb73dc2a3d5..7245a399d33 100644 --- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx @@ -1,17 +1,20 @@ import React, { useEffect, useState } from 'react'; import { DialogTrigger } from 'react-aria-components'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { send } from 'loot-core/src/platform/client/fetch'; +import { useAuth } from '../../auth/AuthProvider'; +import { Permissions } from '../../auth/types'; import { authorizeBank } from '../../gocardless'; import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus'; import { useSimpleFinStatus } from '../../hooks/useSimpleFinStatus'; import { useSyncServerStatus } from '../../hooks/useSyncServerStatus'; import { SvgDotsHorizontalTriple } from '../../icons/v1'; import { theme } from '../../style'; +import { Warning } from '../alerts'; import { Button, ButtonWithLoading } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; import { Link } from '../common/Link'; @@ -21,6 +24,7 @@ import { Paragraph } from '../common/Paragraph'; import { Popover } from '../common/Popover'; import { Text } from '../common/Text'; import { View } from '../common/View'; +import { useMultiuserEnabled } from '../ServerContext'; type CreateAccountProps = { upgradingAccountId?: string; @@ -28,6 +32,7 @@ type CreateAccountProps = { export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { const { t } = useTranslation(); + const syncServerStatus = useSyncServerStatus(); const dispatch = useDispatch(); const [isGoCardlessSetupComplete, setIsGoCardlessSetupComplete] = useState< @@ -36,6 +41,8 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { const [isSimpleFinSetupComplete, setIsSimpleFinSetupComplete] = useState< boolean | null >(null); + const { hasPermission } = useAuth(); + const multiuserEnabled = useMultiuserEnabled(); const onConnectGoCardless = () => { if (!isGoCardlessSetupComplete) { @@ -178,6 +185,9 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { title = t('Link Account'); } + const canSetSecrets = + !multiuserEnabled || hasPermission(Permissions.ADMINISTRATOR); + return ( <Modal name="add-account"> {({ state: { close } }) => ( @@ -223,126 +233,149 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { <View style={{ gap: 10 }}> {syncServerStatus === 'online' ? ( <> - <View - style={{ - flexDirection: 'row', - gap: 10, - alignItems: 'center', - }} - > - <ButtonWithLoading - isDisabled={syncServerStatus !== 'online'} - style={{ - padding: '10px 0', - fontSize: 15, - fontWeight: 600, - flex: 1, - }} - onPress={onConnectGoCardless} - > - {isGoCardlessSetupComplete - ? t('Link bank account with GoCardless') - : t('Set up GoCardless for bank sync')} - </ButtonWithLoading> - {isGoCardlessSetupComplete && ( - <DialogTrigger> - <Button - variant="bare" - aria-label={t('GoCardless menu')} + {canSetSecrets && ( + <> + <View + style={{ + flexDirection: 'row', + gap: 10, + alignItems: 'center', + }} + > + <ButtonWithLoading + isDisabled={syncServerStatus !== 'online'} + style={{ + padding: '10px 0', + fontSize: 15, + fontWeight: 600, + flex: 1, + }} + onPress={onConnectGoCardless} > - <SvgDotsHorizontalTriple - width={15} - height={15} - style={{ transform: 'rotateZ(90deg)' }} - /> - </Button> + {isGoCardlessSetupComplete + ? t('Link bank account with GoCardless') + : t('Set up GoCardless for bank sync')} + </ButtonWithLoading> + {isGoCardlessSetupComplete && ( + <DialogTrigger> + <Button + variant="bare" + aria-label={t('GoCardless menu')} + > + <SvgDotsHorizontalTriple + width={15} + height={15} + style={{ transform: 'rotateZ(90deg)' }} + /> + </Button> - <Popover> - <Menu - onMenuSelect={item => { - if (item === 'reconfigure') { - onGoCardlessReset(); - } - }} - items={[ - { - name: 'reconfigure', - text: t('Reset GoCardless credentials'), - }, - ]} - /> - </Popover> - </DialogTrigger> - )} - </View> - <Text style={{ lineHeight: '1.4em', fontSize: 15 }}> - <strong> - {t('Link a')} <em>{t('European')}</em> {t('bank account')} - </strong>{' '} - {t( - 'to automatically download transactions. GoCardless provides reliable, up-to-date information from hundreds of banks.', - )} - </Text> - - <View - style={{ - flexDirection: 'row', - gap: 10, - marginTop: '18px', - alignItems: 'center', - }} - > - <ButtonWithLoading - isDisabled={syncServerStatus !== 'online'} - isLoading={loadingSimpleFinAccounts} - style={{ - padding: '10px 0', - fontSize: 15, - fontWeight: 600, - flex: 1, - }} - onPress={onConnectSimpleFin} - > - {isSimpleFinSetupComplete - ? t('Link bank account with SimpleFIN') - : t('Set up SimpleFIN for bank sync')} - </ButtonWithLoading> - {isSimpleFinSetupComplete && ( - <DialogTrigger> - <Button variant="bare" aria-label={t('SimpleFIN menu')}> - <SvgDotsHorizontalTriple - width={15} - height={15} - style={{ transform: 'rotateZ(90deg)' }} - /> - </Button> - <Popover> - <Menu - onMenuSelect={item => { - if (item === 'reconfigure') { - onSimpleFinReset(); - } - }} - items={[ - { - name: 'reconfigure', - text: t('Reset SimpleFIN credentials'), - }, - ]} - /> - </Popover> - </DialogTrigger> + <Popover> + <Menu + onMenuSelect={item => { + if (item === 'reconfigure') { + onGoCardlessReset(); + } + }} + items={[ + { + name: 'reconfigure', + text: t('Reset GoCardless credentials'), + }, + ]} + /> + </Popover> + </DialogTrigger> + )} + </View> + <Text style={{ lineHeight: '1.4em', fontSize: 15 }}> + <strong> + {t('Link a')} <em>{t('European')}</em>{' '} + {t('bank account')} + </strong>{' '} + {t( + 'to automatically download transactions. GoCardless provides reliable, up-to-date information from hundreds of banks.', + )} + </Text> + <View + style={{ + flexDirection: 'row', + gap: 10, + marginTop: '18px', + alignItems: 'center', + }} + > + <ButtonWithLoading + isDisabled={syncServerStatus !== 'online'} + isLoading={loadingSimpleFinAccounts} + style={{ + padding: '10px 0', + fontSize: 15, + fontWeight: 600, + flex: 1, + }} + onPress={onConnectSimpleFin} + > + {isSimpleFinSetupComplete + ? t('Link bank account with SimpleFIN') + : t('Set up SimpleFIN for bank sync')} + </ButtonWithLoading> + {isSimpleFinSetupComplete && ( + <DialogTrigger> + <Button + variant="bare" + aria-label={t('SimpleFIN menu')} + > + <SvgDotsHorizontalTriple + width={15} + height={15} + style={{ transform: 'rotateZ(90deg)' }} + /> + </Button> + <Popover> + <Menu + onMenuSelect={item => { + if (item === 'reconfigure') { + onSimpleFinReset(); + } + }} + items={[ + { + name: 'reconfigure', + text: t('Reset SimpleFIN credentials'), + }, + ]} + /> + </Popover> + </DialogTrigger> + )} + </View> + <Text style={{ lineHeight: '1.4em', fontSize: 15 }}> + <strong> + {t('Link a')} <em>{t('North American')}</em> + {t(' bank account')} + </strong>{' '} + {t( + 'to automatically download transactions. SimpleFIN provides reliable, up-to-date information from hundreds of banks.', + )}{' '} + </Text> + </> + )} + {(!isGoCardlessSetupComplete || !isSimpleFinSetupComplete) && + !canSetSecrets && ( + <Warning> + <Trans> + You don't have the required permissions to set up + secrets. Please contact an Admin to configure + </Trans>{' '} + {[ + isGoCardlessSetupComplete ? '' : 'GoCardless', + isSimpleFinSetupComplete ? '' : 'SimpleFin', + ] + .filter(Boolean) + .join(' or ')} + . + </Warning> )} - </View> - <Text style={{ lineHeight: '1.4em', fontSize: 15 }}> - <strong> - {t('Link a')} <em>{t('North American')}</em> - {t(' bank account')} - </strong>{' '} - {t( - 'to automatically download transactions. SimpleFIN provides reliable, up-to-date information from hundreds of banks.', - )}{' '} - </Text> </> ) : ( <> diff --git a/packages/desktop-client/src/components/modals/EditAccess.tsx b/packages/desktop-client/src/components/modals/EditAccess.tsx new file mode 100644 index 00000000000..93dacc800c4 --- /dev/null +++ b/packages/desktop-client/src/components/modals/EditAccess.tsx @@ -0,0 +1,154 @@ +import { useEffect, useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { send } from 'loot-core/platform/client/fetch'; +import { getUserAccessErrors } from 'loot-core/shared/errors'; +import { type Handlers } from 'loot-core/types/handlers'; +import { type UserAccessEntity } from 'loot-core/types/models/userAccess'; + +import { useActions } from '../../hooks/useActions'; +import { styles, theme } from '../../style'; +import { Button } from '../common/Button2'; +import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; +import { Select } from '../common/Select'; +import { Stack } from '../common/Stack'; +import { Text } from '../common/Text'; +import { View } from '../common/View'; +import { FormField, FormLabel } from '../forms'; + +type EditUserAccessProps = { + defaultUserAccess: UserAccessEntity; + onSave?: (userAccess: UserAccessEntity) => void; +}; + +export function EditUserAccess({ + defaultUserAccess, + onSave: originalOnSave, +}: EditUserAccessProps) { + const { t } = useTranslation(); + + const actions = useActions(); + const [userId, setUserId] = useState(defaultUserAccess.userId ?? ''); + const [error, setSetError] = useState(''); + const [availableUsers, setAvailableUsers] = useState<[string, string][]>([]); + + useEffect(() => { + send('access-get-available-users', defaultUserAccess.fileId).then( + (data: Awaited<ReturnType<Handlers['access-get-available-users']>>) => { + if ('error' in data) { + setSetError(data.error); + } else { + setAvailableUsers( + data.map(user => [ + user.userId, + user.displayName + ? `${user.displayName} (${user.userName})` + : user.userName, + ]), + ); + } + }, + ); + }, [defaultUserAccess.fileId, actions]); + + async function onSave(close: () => void) { + const userAccess = { + ...defaultUserAccess, + userId, + }; + + const { error } = await send('access-add', userAccess); + if (!error) { + originalOnSave?.(userAccess); + close(); + } else { + if (error === 'token-expired') { + actions.addNotification({ + type: 'error', + id: 'login-expired', + title: t('Login expired'), + sticky: true, + message: getUserAccessErrors(error), + button: { + title: t('Go to login'), + action: () => { + actions.signOut(); + }, + }, + }); + } else { + setSetError(getUserAccessErrors(error)); + } + } + } + + return ( + <Modal name="edit-access"> + {({ state: { close } }: { state: { close: () => void } }) => ( + <> + <ModalHeader + title={t('User Access')} + rightContent={<ModalCloseButton onPress={close} />} + /> + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title={t('User')} htmlFor="user-field" /> + {availableUsers.length > 0 && ( + <View> + <Select + options={availableUsers} + onChange={(newValue: string) => setUserId(newValue)} + value={userId} + /> + <label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + <Trans>Select a user from the directory</Trans> + </label> + </View> + )} + {availableUsers.length === 0 && ( + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + <Trans>No users available to give access</Trans> + </Text> + )} + </FormField> + </Stack> + + <Stack + direction="row" + justify="flex-end" + align="center" + style={{ marginTop: 20 }} + > + {error && <Text style={{ color: theme.errorText }}>{error}</Text>} + <Button + variant="bare" + style={{ marginRight: 10 }} + onPress={actions.popModal} + > + Cancel + </Button> + <Button + variant="primary" + isDisabled={availableUsers.length === 0} + onPress={() => onSave(close)} + > + {defaultUserAccess.userId ? t('Save') : t('Add')} + </Button> + </Stack> + </> + )} + </Modal> + ); +} diff --git a/packages/desktop-client/src/components/modals/EditUser.tsx b/packages/desktop-client/src/components/modals/EditUser.tsx new file mode 100644 index 00000000000..e8ddccca135 --- /dev/null +++ b/packages/desktop-client/src/components/modals/EditUser.tsx @@ -0,0 +1,415 @@ +import { useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { send } from 'loot-core/platform/client/fetch'; +import { + PossibleRoles, + type UserEntity, +} from 'loot-core/src/types/models/user'; + +import { type BoundActions, useActions } from '../../hooks/useActions'; +import { styles, theme } from '../../style'; +import { Button } from '../common/Button2'; +import { Input } from '../common/Input'; +import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; +import { Select } from '../common/Select'; +import { Stack } from '../common/Stack'; +import { Text } from '../common/Text'; +import { View } from '../common/View'; +import { Checkbox, FormField, FormLabel } from '../forms'; + +type User = UserEntity; + +type EditUserProps = { + defaultUser: User; + onSave: ( + method: 'user-add' | 'user-update', + user: User, + setError: (error: string) => void, + actions: BoundActions, + ) => Promise<void>; +}; + +type EditUserFinanceAppProps = { + defaultUser: User; + onSave: (user: User) => void; +}; + +function useGetUserDirectoryErrors() { + const { t } = useTranslation(); + + function getUserDirectoryErrors(reason: string) { + switch (reason) { + case 'unauthorized': + return t('You are not logged in.'); + case 'token-expired': + return t('Login expired, please login again.'); + case 'user-cant-be-empty': + return t( + 'Please enter a value for the username; the field cannot be empty.', + ); + case 'role-cant-be-empty': + return t('Select a role; the field cannot be empty.'); + case 'user-already-exists': + return t( + 'The username you entered already exists. Please choose a different username.', + ); + case 'not-all-deleted': + return t( + 'Not all users were deleted. Check if one of the selected users is the server owner.', + ); + case 'role-does-not-exists': + return t( + 'Selected role does not exists, possibly a bug? Visit https://actualbudget.org/contact/ for support.', + ); + default: + return t( + 'An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: {{reason}})', + { reason }, + ); + } + } + + return { getUserDirectoryErrors }; +} + +function useSaveUser() { + const { t } = useTranslation(); + const { getUserDirectoryErrors } = useGetUserDirectoryErrors(); + + async function saveUser( + method: 'user-add' | 'user-update', + user: User, + setError: (error: string) => void, + actions: BoundActions, + ): Promise<boolean> { + const { error, id: newId } = (await send(method, user)) || {}; + if (!error) { + if (newId) { + user.id = newId; + } + } else { + setError(getUserDirectoryErrors(error)); + if (error === 'token-expired') { + actions.addNotification({ + type: 'error', + id: 'login-expired', + title: t('Login expired'), + sticky: true, + message: getUserDirectoryErrors(error), + button: { + title: t('Go to login'), + action: () => { + actions.signOut(); + }, + }, + }); + } + + return false; + } + + return true; + } + + return { saveUser }; +} + +export function EditUserFinanceApp({ + defaultUser, + onSave: originalOnSave, +}: EditUserFinanceAppProps) { + const { t } = useTranslation(); + const { saveUser } = useSaveUser(); + + return ( + <Modal name="edit-user"> + {({ state: { close } }) => ( + <> + <ModalHeader + title={ + defaultUser.id + ? t('Edit user {{userName}}', { + userName: defaultUser.displayName ?? defaultUser.userName, + }) + : 'Add user' + } + rightContent={<ModalCloseButton onPress={close} />} + /> + <EditUser + defaultUser={defaultUser} + onSave={async (method, user, setError, actions) => { + if (await saveUser(method, user, setError, actions)) { + originalOnSave(user); + close(); + } + }} + /> + </> + )} + </Modal> + ); +} + +function EditUser({ defaultUser, onSave: originalOnSave }: EditUserProps) { + const { t } = useTranslation(); + + const actions = useActions(); + const [userName, setUserName] = useState<string>(defaultUser.userName ?? ''); + const [displayName, setDisplayName] = useState<string>( + defaultUser.displayName ?? '', + ); + const [enabled, setEnabled] = useState<boolean>(defaultUser.enabled); + const [role, setRole] = useState<string>(defaultUser.role ?? 'BASIC'); + const [error, setError] = useState<string>(''); + + async function onSave() { + if (!userName.trim()) { + setError(t('Username is required.')); + return; + } + if (!role) { + setError(t('Role is required.')); + return; + } + const user: User = { + ...defaultUser, + userName, + displayName, + enabled, + role, + }; + + const method = user.id ? 'user-update' : 'user-add'; + await originalOnSave(method, user, setError, actions); + } + + return ( + <> + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title={t('Username')} htmlFor="name-field" /> + <Input + id="name-field" + value={userName} + onChangeValue={text => setUserName(text)} + style={{ + borderColor: theme.buttonMenuBorder, + }} + /> + <label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + <Trans>The username registered within the OpenID provider.</Trans> + </label> + </FormField> + <View + style={{ + flexDirection: 'row', + alignItems: 'center', + userSelect: 'none', + }} + > + {' '} + <Checkbox + id="enabled-field" + checked={enabled} + disabled={defaultUser.owner} + style={{ + color: defaultUser.owner ? theme.pageTextSubdued : 'inherit', + }} + onChange={() => setEnabled(!enabled)} + /> + <label htmlFor="enabled-field" style={{ userSelect: 'none' }}> + Enabled + </label> + </View> + </Stack> + {defaultUser.owner && ( + <label + style={{ + ...styles.verySmallText, + color: theme.errorText, + marginTop: 5, + }} + > + <Trans> + Change this username with caution; it is the server owner. + </Trans> + </label> + )} + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title={t('Display Name')} htmlFor="displayname-field" /> + <Input + id="displayname-field" + value={displayName} + onChangeValue={text => setDisplayName(text)} + placeholder={t('(Optional)')} + style={{ + borderColor: theme.buttonMenuBorder, + }} + /> + <View + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + <Trans> + If left empty, it will be updated from your OpenID provider on the + user's login, if available there. + </Trans> + </View> + <View + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + When displaying user information, this will be shown instead of + the username. + </Trans> + </View> + </FormField> + </Stack> + <Stack direction="row" style={{ marginTop: 10, width: '100px' }}> + <FormField style={{ flex: 1 }}> + <FormLabel title="Role" htmlFor="role-field" /> + <Select + id="role-field" + disabled={defaultUser.owner} + options={Object.entries(PossibleRoles)} + value={role} + onChange={newValue => setRole(newValue)} + style={{ + borderColor: theme.buttonMenuBorder, + }} + /> + </FormField> + </Stack> + <RoleDescription /> + + <Stack + direction="row" + justify="flex-end" + align="center" + style={{ marginTop: 20 }} + > + {error && <Text style={{ color: theme.errorText }}>{error}</Text>} + <Button + variant="bare" + style={{ marginRight: 10 }} + onPress={actions.popModal} + > + <Trans>Cancel</Trans> + </Button> + <Button variant="primary" onPress={onSave}> + {defaultUser.id ? 'Save' : 'Add'} + </Button> + </Stack> + </> + ); +} + +const RoleDescription = () => { + return ( + <View style={{ paddingTop: 10 }}> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + In our user directory, each user is assigned a specific role that + determines their permissions and capabilities within the system. + </Trans> + </Text> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + Understanding these roles is essential for managing users and + responsibilities effectively. + </Trans> + </Text> + <View style={{ paddingTop: 5 }}> + <label + style={{ + ...styles.altMenuHeaderText, + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans>Basic</Trans> + </label> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + Users with the Basic role can create new budgets and be invited to + collaborate on budgets created by others. + </Trans> + </Text> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + This role is ideal for users who primarily need to manage their own + budgets and participate in shared budget activities. + </Trans> + </Text> + </View> + <View style={{ paddingTop: 10 }}> + <label + style={{ + ...styles.altMenuHeaderText, + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans>Admin</Trans> + </label> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + Can do everything that Basic users can. In addition, they have the + ability to add new users to the directory and access budget files + from all users. + </Trans> + </Text> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + <Trans> + Also can assign ownership of a budget to another person, ensuring + efficient budget management. + </Trans> + </Text> + </View> + </View> + ); +}; diff --git a/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx b/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx index a2c01a79448..d305caf15d5 100644 --- a/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx +++ b/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { getSecretsError } from 'loot-core/shared/errors'; import { send } from 'loot-core/src/platform/client/fetch'; import { Error } from '../alerts'; @@ -31,26 +32,47 @@ export const GoCardlessInitialiseModal = ({ const [secretKey, setSecretKey] = useState(''); const [isValid, setIsValid] = useState(true); const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState( + t('It is required to provide both the secret id and secret key.'), + ); const onSubmit = async (close: () => void) => { if (!secretId || !secretKey) { setIsValid(false); + setError( + t('It is required to provide both the secret id and secret key.'), + ); return; } setIsLoading(true); - await Promise.all([ - send('secret-set', { + let { error, reason } = + (await send('secret-set', { name: 'gocardless_secretId', value: secretId, - }), - send('secret-set', { - name: 'gocardless_secretKey', - value: secretKey, - }), - ]); + })) || {}; + + if (error) { + setIsLoading(false); + setIsValid(false); + setError(getSecretsError(error, reason)); + return; + } else { + ({ error, reason } = + (await send('secret-set', { + name: 'gocardless_secretKey', + value: secretKey, + })) || {}); + if (error) { + setIsLoading(false); + setIsValid(false); + setError(getSecretsError(error, reason)); + return; + } + } + setIsValid(true); onSuccess(); setIsLoading(false); close(); @@ -107,13 +129,7 @@ export const GoCardlessInitialiseModal = ({ /> </FormField> - {!isValid && ( - <Error> - {t( - 'It is required to provide both the secret id and secret key.', - )} - </Error> - )} + {!isValid && <Error>{error}</Error>} </View> <ModalButtons> diff --git a/packages/desktop-client/src/components/modals/OpenIDEnableModal.tsx b/packages/desktop-client/src/components/modals/OpenIDEnableModal.tsx new file mode 100644 index 00000000000..d77bd0781fe --- /dev/null +++ b/packages/desktop-client/src/components/modals/OpenIDEnableModal.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { send } from 'loot-core/platform/client/fetch'; +import * as asyncStorage from 'loot-core/platform/server/asyncStorage'; +import { getOpenIdErrors } from 'loot-core/shared/errors'; +import { type OpenIdConfig } from 'loot-core/types/models/openid'; + +import { useActions } from '../../hooks/useActions'; +import { theme, styles } from '../../style'; +import { Error } from '../alerts'; +import { Button } from '../common/Button2'; +import { Label } from '../common/Label'; +import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; +import { View } from '../common/View'; +import { OpenIdForm } from '../manager/subscribe/OpenIdForm'; +import { useRefreshLoginMethods } from '../ServerContext'; + +type OpenIDEnableModalProps = { + onSave?: () => void; +}; + +export function OpenIDEnableModal({ + onSave: originalOnSave, +}: OpenIDEnableModalProps) { + const { t } = useTranslation(); + + const [error, setError] = useState(''); + const actions = useActions(); + const { closeBudget } = useActions(); + const refreshLoginMethods = useRefreshLoginMethods(); + + async function onSave(config: OpenIdConfig) { + try { + const { error } = (await send('enable-openid', { openId: config })) || {}; + if (!error) { + originalOnSave?.(); + try { + await refreshLoginMethods(); + await asyncStorage.removeItem('user-token'); + await closeBudget(); + } catch (e) { + console.error('Failed to cleanup after OpenID enable:', e); + setError( + t( + 'OpenID was enabled but cleanup failed. Please refresh the application.', + ), + ); + } + } else { + setError(getOpenIdErrors(error)); + } + } catch (e) { + console.error('Failed to enable OpenID:', e); + setError(t('Failed to enable OpenID. Please try again.')); + } + } + + return ( + <Modal name="enable-openid"> + {({ state: { close } }) => ( + <> + <ModalHeader + title={t('Enable OpenID')} + rightContent={<ModalCloseButton onPress={close} />} + /> + + <View style={{ flexDirection: 'column' }}> + <OpenIdForm + onSetOpenId={onSave} + otherButtons={[ + <Button + key="cancel" + variant="bare" + style={{ marginRight: 10 }} + onPress={actions.popModal} + > + <Trans>Cancel</Trans> + </Button>, + ]} + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + paddingTop: 5, + }} + title={t('After enabling openid all sessions will be closed')} + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + title={t('The first user to login will become the server owner')} + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.warningText, + }} + title={t('The current password will be disabled')} + /> + + {error && <Error>{error}</Error>} + </View> + </> + )} + </Modal> + ); +} diff --git a/packages/desktop-client/src/components/modals/PasswordEnableModal.tsx b/packages/desktop-client/src/components/modals/PasswordEnableModal.tsx new file mode 100644 index 00000000000..3e9d83c27fc --- /dev/null +++ b/packages/desktop-client/src/components/modals/PasswordEnableModal.tsx @@ -0,0 +1,145 @@ +import { useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { send } from 'loot-core/platform/client/fetch'; +import * as asyncStorage from 'loot-core/src/platform/server/asyncStorage'; + +import { useActions } from '../../hooks/useActions'; +import { theme, styles } from '../../style'; +import { Error as ErrorAlert } from '../alerts'; +import { Button } from '../common/Button2'; +import { Label } from '../common/Label'; +import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; +import { View } from '../common/View'; +import { FormField } from '../forms'; +import { + ConfirmOldPasswordForm, + ConfirmPasswordForm, +} from '../manager/subscribe/ConfirmPasswordForm'; +import { + useAvailableLoginMethods, + useMultiuserEnabled, + useRefreshLoginMethods, +} from '../ServerContext'; + +type PasswordEnableModalProps = { + onSave?: () => void; +}; + +export function PasswordEnableModal({ + onSave: originalOnSave, +}: PasswordEnableModalProps) { + const { t } = useTranslation(); + + const [error, setError] = useState<string | null>(null); + const { closeBudget, popModal } = useActions(); + const multiuserEnabled = useMultiuserEnabled(); + const availableLoginMethods = useAvailableLoginMethods(); + const refreshLoginMethods = useRefreshLoginMethods(); + + const errorMessages = { + 'invalid-password': t('Invalid Password'), + 'password-match': t('Passwords do not match'), + 'network-failure': t('Unable to contact the server'), + 'unable-to-change-file-config-enabled': t( + 'Unable to disable OpenID. Please update the config.json file in this case.', + ), + }; + + function getErrorMessage(error: string): string { + return ( + errorMessages[error as keyof typeof errorMessages] || + t('Internal server error') + ); + } + + async function onSetPassword(password: string) { + setError(null); + const { error } = (await send('enable-password', { password })) || {}; + if (!error) { + originalOnSave?.(); + await refreshLoginMethods(); + await asyncStorage.removeItem('user-token'); + await closeBudget(); + } else { + setError(getErrorMessage(error)); + } + } + + return ( + <Modal name="enable-password-auth"> + {({ state: { close } }) => ( + <> + <ModalHeader + title={t('Revert to server password')} + rightContent={<ModalCloseButton onPress={close} />} + /> + + <View style={{ flexDirection: 'column' }}> + <FormField style={{ flex: 1 }}> + {!availableLoginMethods.some( + login => login.method === 'password', + ) && ( + <ConfirmPasswordForm + buttons={ + <Button + variant="bare" + style={{ fontSize: 15, marginRight: 10 }} + onPress={() => popModal()} + > + <Trans>Cancel</Trans> + </Button> + } + onSetPassword={onSetPassword} + onError={(error: string) => setError(getErrorMessage(error))} + /> + )} + {availableLoginMethods.some( + login => login.method === 'password', + ) && ( + <ConfirmOldPasswordForm + buttons={ + <Button + variant="bare" + style={{ fontSize: 15, marginRight: 10 }} + onPress={() => popModal()} + > + <Trans>Cancel</Trans> + </Button> + } + onSetPassword={onSetPassword} + /> + )} + </FormField> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + paddingTop: 5, + }} + title={t('Type the server password to disable OpenID')} + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + paddingTop: 5, + }} + title={t('After disabling OpenID all sessions will be closed')} + /> + {multiuserEnabled && ( + <Label + style={{ + ...styles.verySmallText, + color: theme.errorText, + }} + title={t('Multi-user will not work after disabling')} + /> + )} + {error && <ErrorAlert>{error}</ErrorAlert>} + </View> + </> + )} + </Modal> + ); +} diff --git a/packages/desktop-client/src/components/modals/SimpleFinInitialiseModal.tsx b/packages/desktop-client/src/components/modals/SimpleFinInitialiseModal.tsx index 54fac7a98a2..08058f5b525 100644 --- a/packages/desktop-client/src/components/modals/SimpleFinInitialiseModal.tsx +++ b/packages/desktop-client/src/components/modals/SimpleFinInitialiseModal.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { getSecretsError } from 'loot-core/shared/errors'; import { send } from 'loot-core/src/platform/client/fetch'; import { Error } from '../alerts'; @@ -29,6 +30,7 @@ export const SimpleFinInitialiseModal = ({ const [token, setToken] = useState(''); const [isValid, setIsValid] = useState(true); const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(t('It is required to provide a token.')); const onSubmit = async (close: () => void) => { if (!token) { @@ -38,12 +40,18 @@ export const SimpleFinInitialiseModal = ({ setIsLoading(true); - await send('secret-set', { - name: 'simplefin_token', - value: token, - }); + const { error, reason } = + (await send('secret-set', { + name: 'simplefin_token', + value: token, + })) || {}; - onSuccess(); + if (error) { + setIsValid(false); + setError(getSecretsError(error, reason)); + } else { + onSuccess(); + } setIsLoading(false); close(); }; @@ -84,7 +92,7 @@ export const SimpleFinInitialiseModal = ({ /> </FormField> - {!isValid && <Error>It is required to provide a token.</Error>} + {!isValid && <Error>{error}</Error>} </View> <ModalButtons> diff --git a/packages/desktop-client/src/components/modals/TransferOwnership.tsx b/packages/desktop-client/src/components/modals/TransferOwnership.tsx new file mode 100644 index 00000000000..bb2a198c48c --- /dev/null +++ b/packages/desktop-client/src/components/modals/TransferOwnership.tsx @@ -0,0 +1,206 @@ +import { useEffect, useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; + +import { addNotification, closeAndLoadBudget } from 'loot-core/client/actions'; +import { type State } from 'loot-core/client/state-types'; +import { send } from 'loot-core/platform/client/fetch'; +import { getUserAccessErrors } from 'loot-core/shared/errors'; +import { type Budget } from 'loot-core/types/budget'; +import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file'; +import { type Handlers } from 'loot-core/types/handlers'; + +import { useActions } from '../../hooks/useActions'; +import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { styles, theme } from '../../style'; +import { Button } from '../common/Button2'; +import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; +import { Select } from '../common/Select'; +import { Stack } from '../common/Stack'; +import { Text } from '../common/Text'; +import { View } from '../common/View'; +import { FormField, FormLabel } from '../forms'; + +type TransferOwnershipProps = { + onSave?: () => void; +}; + +export function TransferOwnership({ + onSave: originalOnSave, +}: TransferOwnershipProps) { + const { t } = useTranslation(); + + const userData = useSelector((state: State) => state.user.data); + const actions = useActions(); + const [userId, setUserId] = useState(''); + const [error, setError] = useState<string | null>(null); + const [availableUsers, setAvailableUsers] = useState<[string, string][]>([]); + const [cloudFileId] = useMetadataPref('cloudFileId'); + const allFiles = useSelector(state => state.budgets.allFiles || []); + const remoteFiles = allFiles.filter( + f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', + ) as (SyncedLocalFile | RemoteFile)[]; + const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); + const dispatch = useDispatch(); + const [isTransferring, setIsTransferring] = useState(false); + + useEffect(() => { + send('users-get').then( + (data: Awaited<ReturnType<Handlers['users-get']>>) => { + if (!data) { + setAvailableUsers([]); + } else if ('error' in data) { + addNotification({ + type: 'error', + title: t('Error getting users'), + message: t( + 'Failed to complete ownership transfer. Please try again.', + ), + sticky: true, + }); + } else { + setAvailableUsers( + data + .filter(f => currentFile?.owner !== f.id) + .map(user => [ + user.id, + user.displayName + ? `${user.displayName} (${user.userName})` + : user.userName, + ]), + ); + } + }, + ); + }, [userData?.userId, currentFile?.owner, t]); + + async function onSave() { + if (cloudFileId) { + const response = await send('transfer-ownership', { + fileId: cloudFileId as string, + newUserId: userId, + }); + const { error } = response || {}; + if (!error) { + originalOnSave?.(); + } else { + setError(getUserAccessErrors(error)); + } + } else { + setError(t('Cloud file ID is missing.')); + } + } + + return ( + <Modal name="transfer-ownership"> + {({ state: { close } }: { state: { close: () => void } }) => ( + <> + <ModalHeader + title={t('Transfer ownership')} + rightContent={<ModalCloseButton onPress={close} />} + /> + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title={t('User')} htmlFor="user-field" /> + {availableUsers.length > 0 && ( + <View> + <Select + options={availableUsers} + onChange={(newValue: string) => { + setUserId(newValue); + }} + value={userId} + defaultLabel={t('Select a user')} + /> + <label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + {t( + 'Select a user from the directory to designate as the new budget owner.', + )} + </label> + <label + style={{ + ...styles.verySmallText, + color: theme.errorText, + marginTop: 5, + }} + > + {t( + 'This action is irreversible, ownership of this budget file will only be able to be transferred by the server administrator or new owner.', + )} + </label> + <label + style={{ + ...styles.verySmallText, + color: theme.errorText, + marginTop: 5, + }} + > + {t('Proceed with caution.')} + </label> + </View> + )} + {availableUsers.length === 0 && ( + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + {t('No users available')} + </Text> + )} + </FormField> + </Stack> + + <Stack + direction="row" + justify="flex-end" + align="center" + style={{ marginTop: 20 }} + > + {error && <Text style={{ color: theme.errorText }}>{error}</Text>} + <Button style={{ marginRight: 10 }} onPress={actions.popModal}> + <Trans>Cancel</Trans> + </Button> + + <Button + variant="primary" + isDisabled={ + availableUsers.length === 0 || !userId || isTransferring + } + onPress={async () => { + setIsTransferring(true); + try { + await onSave(); + await dispatch( + closeAndLoadBudget((currentFile as Budget).id), + ); + close(); + } catch (error) { + addNotification({ + type: 'error', + title: t('Failed to transfer ownership'), + message: t( + 'Failed to complete ownership transfer. Please try again.', + ), + sticky: true, + }); + setIsTransferring(false); + } + }} + > + {isTransferring ? t('Transferring...') : t('Transfer ownership')} + </Button> + </Stack> + </> + )} + </Modal> + ); +} diff --git a/packages/desktop-client/src/components/responsive/wide.ts b/packages/desktop-client/src/components/responsive/wide.ts index 5b54b8eac01..94eb01e43c5 100644 --- a/packages/desktop-client/src/components/responsive/wide.ts +++ b/packages/desktop-client/src/components/responsive/wide.ts @@ -6,3 +6,5 @@ export { GoCardlessLink } from '../gocardless/GoCardlessLink'; export { Account as Accounts } from '../accounts/Account'; export { Account } from '../accounts/Account'; + +export { UserDirectoryPage } from '../admin/UserDirectory/UserDirectoryPage'; diff --git a/packages/desktop-client/src/components/settings/AuthSettings.tsx b/packages/desktop-client/src/components/settings/AuthSettings.tsx new file mode 100644 index 00000000000..229a5ed92b4 --- /dev/null +++ b/packages/desktop-client/src/components/settings/AuthSettings.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; + +import { pushModal } from 'loot-core/client/actions'; + +import { useFeatureFlag } from '../../hooks/useFeatureFlag'; +import { theme } from '../../style'; +import { Button } from '../common/Button2'; +import { Label } from '../common/Label'; +import { Text } from '../common/Text'; +import { useMultiuserEnabled, useLoginMethod } from '../ServerContext'; + +import { Setting } from './UI'; + +export function AuthSettings() { + const { t } = useTranslation(); + + const multiuserEnabled = useMultiuserEnabled(); + const loginMethod = useLoginMethod(); + const dispatch = useDispatch(); + const openidAuthFeatureFlag = useFeatureFlag('openidAuth'); + + return openidAuthFeatureFlag === true ? ( + <Setting + primaryAction={ + <> + <label> + <Trans>OpenID is</Trans>{' '} + <label style={{ fontWeight: 'bold' }}> + {loginMethod === 'openid' ? t('enabled') : t('disabled')} + </label> + </label> + {loginMethod === 'password' && ( + <> + <Button + id="start-using" + style={{ + marginTop: '10px', + }} + variant="normal" + onPress={() => + dispatch( + pushModal('enable-openid', { + onSave: async () => {}, + }), + ) + } + > + Start using OpenID + </Button> + <Label + style={{ paddingTop: 5 }} + title={t('OpenID is required to enable multi-user mode.')} + /> + </> + )} + {loginMethod !== 'password' && ( + <> + <Button + style={{ + marginTop: '10px', + }} + variant="normal" + onPress={() => + dispatch( + pushModal('enable-password-auth', { + onSave: async () => {}, + }), + ) + } + > + <Trans>Disable OpenID</Trans> + </Button> + {multiuserEnabled && ( + <label style={{ paddingTop: 5, color: theme.errorText }}> + <Trans> + Disabling OpenID will deactivate multi-user mode. + </Trans> + </label> + )} + </> + )} + </> + } + > + <Text> + <Trans> + <strong>Authentication method</strong> modifies how users log in to + the system. + </Trans> + </Text> + </Setting> + ) : null; +} diff --git a/packages/desktop-client/src/components/settings/Experimental.tsx b/packages/desktop-client/src/components/settings/Experimental.tsx index 53ebba44eaa..94a5f1182fc 100644 --- a/packages/desktop-client/src/components/settings/Experimental.tsx +++ b/packages/desktop-client/src/components/settings/Experimental.tsx @@ -96,6 +96,12 @@ export function ExperimentalFeatures() { > <Trans>Context menus</Trans> </FeatureToggle> + <FeatureToggle + flag="openidAuth" + feedbackLink="https://github.com/actualbudget/actual/issues/524" + > + <Trans>OpenID authentication method</Trans> + </FeatureToggle> </View> ) : ( <Link diff --git a/packages/desktop-client/src/components/settings/index.tsx b/packages/desktop-client/src/components/settings/index.tsx index 962ce69f999..a0a04890a98 100644 --- a/packages/desktop-client/src/components/settings/index.tsx +++ b/packages/desktop-client/src/components/settings/index.tsx @@ -24,6 +24,7 @@ import { Page } from '../Page'; import { useResponsive } from '../responsive/ResponsiveProvider'; import { useServerVersion } from '../ServerContext'; +import { AuthSettings } from './AuthSettings'; import { Backups } from './Backups'; import { BudgetTypeSettings } from './BudgetTypeSettings'; import { EncryptionSettings } from './Encryption'; @@ -182,6 +183,7 @@ export function Settings() { <About /> <ThemeSettings /> <FormatSettings /> + <AuthSettings /> <EncryptionSettings /> <BudgetTypeSettings /> {isElectron() && <Backups />} diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx index a654e329d26..5f5ed4c4170 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx @@ -22,6 +22,7 @@ import { } from 'loot-core/src/shared/transactions'; import { integerToCurrency } from 'loot-core/src/shared/util'; +import { AuthProvider } from '../../auth/AuthProvider'; import { SelectedProviderWithItems } from '../../hooks/useSelected'; import { SplitsExpandedProvider } from '../../hooks/useSplitsExpanded'; import { ResponsiveProvider } from '../responsive/ResponsiveProvider'; @@ -148,33 +149,35 @@ function LiveTransactionTable(props) { return ( <TestProvider> <ResponsiveProvider> - <SpreadsheetProvider> - <SchedulesProvider> - <SelectedProviderWithItems - name="transactions" - items={transactions} - fetchAllIds={() => transactions.map(t => t.id)} - > - <SplitsExpandedProvider> - <TransactionTable - {...props} - transactions={transactions} - loadMoreTransactions={() => {}} - commonPayees={[]} - payees={payees} - addNotification={n => console.log(n)} - onSave={onSave} - onSplit={onSplit} - onAdd={onAdd} - onAddSplit={onAddSplit} - onCreatePayee={onCreatePayee} - showSelection={true} - allowSplitTransaction={true} - /> - </SplitsExpandedProvider> - </SelectedProviderWithItems> - </SchedulesProvider> - </SpreadsheetProvider> + <AuthProvider> + <SpreadsheetProvider> + <SchedulesProvider> + <SelectedProviderWithItems + name="transactions" + items={transactions} + fetchAllIds={() => transactions.map(t => t.id)} + > + <SplitsExpandedProvider> + <TransactionTable + {...props} + transactions={transactions} + loadMoreTransactions={() => {}} + commonPayees={[]} + payees={payees} + addNotification={n => console.log(n)} + onSave={onSave} + onSplit={onSplit} + onAdd={onAdd} + onAddSplit={onAddSplit} + onCreatePayee={onCreatePayee} + showSelection={true} + allowSplitTransaction={true} + /> + </SplitsExpandedProvider> + </SelectedProviderWithItems> + </SchedulesProvider> + </SpreadsheetProvider> + </AuthProvider> </ResponsiveProvider> </TestProvider> ); diff --git a/packages/desktop-client/src/hooks/useFeatureFlag.ts b/packages/desktop-client/src/hooks/useFeatureFlag.ts index f1883a8649b..7b8b2e27f33 100644 --- a/packages/desktop-client/src/hooks/useFeatureFlag.ts +++ b/packages/desktop-client/src/hooks/useFeatureFlag.ts @@ -7,6 +7,7 @@ const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = { actionTemplating: false, upcomingLengthAdjustment: false, contextMenus: false, + openidAuth: false, }; export function useFeatureFlag(name: FeatureFlag): boolean { diff --git a/packages/desktop-client/src/hooks/useSyncServerStatus.ts b/packages/desktop-client/src/hooks/useSyncServerStatus.ts index ae5108a82ce..f9a2351d3fd 100644 --- a/packages/desktop-client/src/hooks/useSyncServerStatus.ts +++ b/packages/desktop-client/src/hooks/useSyncServerStatus.ts @@ -14,5 +14,5 @@ export function useSyncServerStatus(): SyncServerStatus { return 'no-server'; } - return !userData || userData.offline ? 'offline' : 'online'; + return !userData || userData?.offline ? 'offline' : 'online'; } diff --git a/packages/desktop-client/src/index.tsx b/packages/desktop-client/src/index.tsx index 8d3e585a1c4..296e6f07ee1 100644 --- a/packages/desktop-client/src/index.tsx +++ b/packages/desktop-client/src/index.tsx @@ -27,6 +27,7 @@ import { initialState as initialAppState } from 'loot-core/src/client/reducers/a import { send } from 'loot-core/src/platform/client/fetch'; import { q } from 'loot-core/src/shared/query'; +import { AuthProvider } from './auth/AuthProvider'; import { App } from './components/App'; import { ServerProvider } from './components/ServerContext'; import { handleGlobalEvents } from './global-events'; @@ -104,7 +105,9 @@ const root = createRoot(container); root.render( <Provider store={store}> <ServerProvider> - <App /> + <AuthProvider> + <App /> + </AuthProvider> </ServerProvider> </Provider>, ); diff --git a/packages/desktop-client/vite.config.mts b/packages/desktop-client/vite.config.mts index b61316d3b36..917e93ee37b 100644 --- a/packages/desktop-client/vite.config.mts +++ b/packages/desktop-client/vite.config.mts @@ -158,6 +158,13 @@ export default defineConfig(async ({ mode }) => { '**/*.{js,css,html,txt,wasm,sql,sqlite,ico,png,woff2,webmanifest}', ], ignoreURLParametersMatching: [/^v$/], + navigateFallback: '/index.html', + navigateFallbackDenylist: [ + /^\/account\/.*$/, + /^\/admin\/.*$/, + /^\/secret\/.*$/, + /^\/openid\/.*$/, + ], }, }), injectShims(), diff --git a/packages/desktop-electron/index.ts b/packages/desktop-electron/index.ts index 3a9d4e86089..3a910e05573 100644 --- a/packages/desktop-electron/index.ts +++ b/packages/desktop-electron/index.ts @@ -1,4 +1,5 @@ import fs from 'fs'; +import { createServer, Server } from 'http'; import path from 'path'; import { @@ -54,6 +55,47 @@ if (!isDev || !process.env.ACTUAL_DATA_DIR) { let clientWin: BrowserWindow | null; let serverProcess: UtilityProcess | null; +let oAuthServer: ReturnType<typeof createServer> | null; + +const createOAuthServer = async () => { + const port = 3010; + console.log(`OAuth server running on port: ${port}`); + + if (oAuthServer) { + return { url: `http://localhost:${port}`, server: oAuthServer }; + } + + return new Promise<{ url: string; server: Server }>(resolve => { + const server = createServer((req, res) => { + const query = new URL(req.url || '', `http://localhost:${port}`) + .searchParams; + + const code = query.get('token'); + if (code && clientWin) { + if (isDev) { + clientWin.loadURL(`http://localhost:3001/openid-cb?token=${code}`); + } else { + clientWin.loadURL(`app://actual/openid-cb?token=${code}`); + } + + // Respond to the browser + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('OpenID login successful! You can close this tab.'); + + // Clean up the server after receiving the code + server.close(); + } else { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('No token received.'); + } + }); + + server.listen(port, '127.0.0.1', () => { + resolve({ url: `http://localhost:${port}`, server }); + }); + }); +}; + if (isDev) { process.traceProcessWarnings = true; } @@ -355,6 +397,12 @@ ipcMain.on('get-bootstrap-data', event => { event.returnValue = payload; }); +ipcMain.handle('start-oauth-server', async () => { + const { url, server: newServer } = await createOAuthServer(); + oAuthServer = newServer; + return url; +}); + ipcMain.handle('restart-server', () => { if (serverProcess) { serverProcess.kill(); diff --git a/packages/desktop-electron/preload.ts b/packages/desktop-electron/preload.ts index 1f98a39096a..85b282dcb12 100644 --- a/packages/desktop-electron/preload.ts +++ b/packages/desktop-electron/preload.ts @@ -13,7 +13,6 @@ contextBridge.exposeInMainWorld('Actual', { IS_DEV, ACTUAL_VERSION: VERSION, logToTerminal: console.log, - ipcConnect: ( func: (payload: { on: IpcRenderer['on']; @@ -30,6 +29,8 @@ contextBridge.exposeInMainWorld('Actual', { }); }, + startOAuthServer: () => ipcRenderer.invoke('start-oauth-server'), + relaunch: () => { ipcRenderer.invoke('relaunch'); }, diff --git a/packages/loot-core/src/client/reducers/budgets.ts b/packages/loot-core/src/client/reducers/budgets.ts index 9465fa0bda6..70d9f535f11 100644 --- a/packages/loot-core/src/client/reducers/budgets.ts +++ b/packages/loot-core/src/client/reducers/budgets.ts @@ -48,6 +48,7 @@ function reconcileFiles( deleted: false, state: 'unknown', hasKey: true, + owner: '', }; } @@ -66,6 +67,8 @@ function reconcileFiles( encryptKeyId: remote.encryptKeyId, hasKey: remote.hasKey, state: 'synced', + owner: remote.owner, + usersWithAccess: remote.usersWithAccess, }; } else { return { @@ -77,6 +80,8 @@ function reconcileFiles( encryptKeyId: remote.encryptKeyId, hasKey: remote.hasKey, state: 'detached', + owner: remote.owner, + usersWithAccess: remote.usersWithAccess, }; } } else { @@ -87,6 +92,7 @@ function reconcileFiles( deleted: false, state: 'broken', hasKey: true, + owner: '', }; } } else { @@ -108,6 +114,8 @@ function reconcileFiles( encryptKeyId: f.encryptKeyId, hasKey: f.hasKey, state: 'remote', + owner: f.owner, + usersWithAccess: f.usersWithAccess, }; }), ) diff --git a/packages/loot-core/src/client/shared-listeners.ts b/packages/loot-core/src/client/shared-listeners.ts index 93f56d15cc1..eb970fe6026 100644 --- a/packages/loot-core/src/client/shared-listeners.ts +++ b/packages/loot-core/src/client/shared-listeners.ts @@ -276,6 +276,18 @@ export function listenForSyncEvent(actions, store) { case 'network': // Show nothing break; + case 'token-expired': + notif = { + title: 'Login expired', + message: 'Please login again.', + sticky: true, + id: 'login-expired', + button: { + title: 'Go to login', + action: () => actions.signOut(), + }, + }; + break; default: console.trace('unknown error', info); notif = { diff --git a/packages/loot-core/src/client/state-types/modals.d.ts b/packages/loot-core/src/client/state-types/modals.d.ts index 9a415988fcf..8e09e5ba3e6 100644 --- a/packages/loot-core/src/client/state-types/modals.d.ts +++ b/packages/loot-core/src/client/state-types/modals.d.ts @@ -8,6 +8,8 @@ import type { TransactionEntity, } from '../../types/models'; import type { NewRuleEntity, RuleEntity } from '../../types/models/rule'; +import { type NewUserEntity, type UserEntity } from '../../types/models/user'; +import { type UserAccessEntity } from '../../types/models/userAccess'; import type { EmptyObject, StripNever } from '../../types/util'; import type * as constants from '../constants'; export type ModalType = keyof FinanceModals; @@ -312,6 +314,23 @@ type FinanceModals = { message?: string; onConfirm: () => void; }; + 'edit-user': { + user: UserEntity | NewUserEntity; + onSave: (rule: UserEntity) => void; + }; + 'edit-access': { + access: UserAccessEntity | NewUserAccessEntity; + onSave: (rule: UserEntity) => void; + }; + 'transfer-ownership': { + onSave: () => void; + }; + 'enable-openid': { + onSave: () => void; + }; + 'enable-password-auth': { + onSave: () => void; + }; 'confirm-unlink-account': { accountName: string; onUnlink: () => void; diff --git a/packages/loot-core/src/server/admin/app.ts b/packages/loot-core/src/server/admin/app.ts new file mode 100644 index 00000000000..121236c6a49 --- /dev/null +++ b/packages/loot-core/src/server/admin/app.ts @@ -0,0 +1,191 @@ +// @ts-strict-ignore +import * as asyncStorage from '../../platform/server/asyncStorage'; +import { UserAvailable, UserEntity } from '../../types/models/user'; +import { createApp } from '../app'; +import { del, get, patch, post } from '../post'; +import { getServer } from '../server-config'; + +import { AdminHandlers } from './types/handlers'; + +// Expose functions to the client +export const app = createApp<AdminHandlers>(); + +app.method('user-delete-all', async function (ids) { + const userToken = await asyncStorage.getItem('user-token'); + if (userToken) { + try { + const res = await del( + getServer().BASE_SERVER + '/admin/users', + { + ids, + }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); + + if (res) { + return res; + } + } catch (err) { + return { error: err.reason }; + } + } + + return { someDeletionsFailed: true }; +}); + +app.method('users-get', async function () { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + const res = await get(getServer().BASE_SERVER + '/admin/users/', { + headers: { + 'X-ACTUAL-TOKEN': userToken, + }, + }); + + if (res) { + try { + const list = JSON.parse(res) as UserEntity[]; + return list; + } catch (err) { + return { error: 'Failed to parse response: ' + err.message }; + } + } + } + + return null; +}); + +app.method('user-add', async function (user) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + try { + const res = await post(getServer().BASE_SERVER + '/admin/users/', user, { + 'X-ACTUAL-TOKEN': userToken, + }); + + return res as UserEntity; + } catch (err) { + return { error: err.reason }; + } + } + + return null; +}); + +app.method('user-update', async function (user) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + try { + const res = await patch(getServer().BASE_SERVER + '/admin/users/', user, { + 'X-ACTUAL-TOKEN': userToken, + }); + + return res as UserEntity; + } catch (err) { + return { error: err.reason }; + } + } + + return null; +}); + +app.method('access-add', async function (access) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + try { + await post(getServer().BASE_SERVER + '/admin/access/', access, { + 'X-ACTUAL-TOKEN': userToken, + }); + + return {}; + } catch (err) { + return { error: err.reason }; + } + } + + return null; +}); + +app.method('access-delete-all', async function ({ fileId, ids }) { + const userToken = await asyncStorage.getItem('user-token'); + if (userToken) { + try { + const res = await del( + getServer().BASE_SERVER + `/admin/access?fileId=${fileId}`, + { + token: userToken, + ids, + }, + ); + + if (res) { + return res; + } + } catch (err) { + return { error: err.reason }; + } + } + + return { someDeletionsFailed: true }; +}); + +app.method('access-get-available-users', async function (fileId) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + const res = await get( + `${getServer().BASE_SERVER + '/admin/access/users'}?fileId=${fileId}`, + { + headers: { + 'X-ACTUAL-TOKEN': userToken, + }, + }, + ); + + if (res) { + try { + return JSON.parse(res) as UserAvailable[]; + } catch (err) { + return { error: 'Failed to parse response: ' + err.message }; + } + } + } + + return []; +}); + +app.method('transfer-ownership', async function ({ fileId, newUserId }) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + try { + await post( + getServer().BASE_SERVER + '/admin/access/transfer-ownership/', + { fileId, newUserId }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); + } catch (err) { + return { error: err.reason }; + } + } + + return {}; +}); + +app.method('owner-created', async function () { + const res = await get(getServer().BASE_SERVER + '/admin/owner-created/'); + + if (res) { + return JSON.parse(res) as boolean; + } + + return null; +}); diff --git a/packages/loot-core/src/server/admin/types/handlers.ts b/packages/loot-core/src/server/admin/types/handlers.ts new file mode 100644 index 00000000000..ec99a415925 --- /dev/null +++ b/packages/loot-core/src/server/admin/types/handlers.ts @@ -0,0 +1,44 @@ +import { UserAvailable, UserEntity } from '../../../types/models/user'; +import { NewUserAccessEntity } from '../../../types/models/userAccess'; + +export interface AdminHandlers { + 'users-get': () => Promise<UserEntity[] | null | { error: string }>; + + 'user-delete-all': ( + ids: string[], + ) => Promise<{ someDeletionsFailed: boolean; ids?: number[] }>; + + 'user-add': ( + user: Omit<UserEntity, 'id'>, + ) => Promise<{ error?: string } | { id: string }>; + + 'user-update': ( + user: Omit<UserEntity, 'id'>, + ) => Promise<{ error?: string } | { id: string }>; + + 'access-add': ( + user: NewUserAccessEntity, + ) => Promise<{ error?: string } | Record<string, never>>; + + 'access-delete-all': ({ + fileId, + ids, + }: { + fileId: string; + ids: string[]; + }) => Promise<{ someDeletionsFailed: boolean; ids?: number[] }>; + + 'access-get-available-users': ( + fileId: string, + ) => Promise<UserAvailable[] | { error: string }>; + + 'transfer-ownership': ({ + fileId, + newUserId, + }: { + fileId: string; + newUserId: string; + }) => Promise<{ error?: string } | Record<string, never>>; + + 'owner-created': () => Promise<boolean>; +} diff --git a/packages/loot-core/src/server/api-models.ts b/packages/loot-core/src/server/api-models.ts index ebd68e8d47f..b6923bcc832 100644 --- a/packages/loot-core/src/server/api-models.ts +++ b/packages/loot-core/src/server/api-models.ts @@ -135,6 +135,8 @@ export const remoteFileModel = { name: file.name, encryptKeyId: file.encryptKeyId, hasKey: file.hasKey, + owner: file.owner, + usersWithAccess: file.usersWithAccess, }; }, diff --git a/packages/loot-core/src/server/cloud-storage.ts b/packages/loot-core/src/server/cloud-storage.ts index 010dbd019d0..c382a60d54a 100644 --- a/packages/loot-core/src/server/cloud-storage.ts +++ b/packages/loot-core/src/server/cloud-storage.ts @@ -22,6 +22,12 @@ import { getServer } from './server-config'; const UPLOAD_FREQUENCY_IN_DAYS = 7; +export interface UsersWithAccess { + userId: string; + userName: string; + displayName: string; + owner: boolean; +} export interface RemoteFile { deleted: boolean; fileId: string; @@ -29,10 +35,24 @@ export interface RemoteFile { name: string; encryptKeyId: string; hasKey: boolean; + owner: string; + usersWithAccess: UsersWithAccess[]; } async function checkHTTPStatus(res) { if (res.status !== 200) { + if (res.status === 403) { + try { + const text = await res.text(); + const data = JSON.parse(text)?.data; + if (data?.reason === 'token-expired') { + await asyncStorage.removeItem('user-token'); + throw new HTTPError(403, 'token-expired'); + } + } catch (e) { + if (e instanceof HTTPError) throw e; + } + } return res.text().then(str => { throw new HTTPError(res.status, str); }); @@ -375,6 +395,38 @@ export async function listRemoteFiles(): Promise<RemoteFile[] | null> { })); } +export async function getRemoteFile( + fileId: string, +): Promise<RemoteFile | null> { + const userToken = await asyncStorage.getItem('user-token'); + if (!userToken) { + return null; + } + + let res; + try { + res = await fetchJSON(getServer().SYNC_SERVER + '/get-user-file-info', { + headers: { + 'X-ACTUAL-TOKEN': userToken, + 'X-ACTUAL-FILE-ID': fileId, + }, + }); + } catch (e) { + console.log('Unexpected error fetching file from server', e); + return null; + } + + if (res.status === 'error') { + console.log('Error fetching file from server', res); + return null; + } + + return { + ...res.data, + hasKey: encryption.hasKey(res.data.encryptKeyId), + }; +} + export async function download(fileId) { const userToken = await asyncStorage.getItem('user-token'); const syncServer = getServer().SYNC_SERVER; diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts index 059869d935d..efeafaf6213 100644 --- a/packages/loot-core/src/server/main.ts +++ b/packages/loot-core/src/server/main.ts @@ -19,6 +19,7 @@ import { q, Query } from '../shared/query'; import { amountToInteger, stringToInteger } from '../shared/util'; import { type Budget } from '../types/budget'; import { Handlers } from '../types/handlers'; +import { OpenIdConfig } from '../types/models/openid'; import { exportToCSV, exportQueryToCSV } from './accounts/export-to-csv'; import * as link from './accounts/link'; @@ -27,6 +28,7 @@ import { getStartingBalancePayee } from './accounts/payees'; import * as bankSync from './accounts/sync'; import * as rules from './accounts/transaction-rules'; import { batchUpdateTransactions } from './accounts/transactions'; +import { app as adminApp } from './admin/app'; import { installAPI } from './api'; import { runQuery as aqlQuery } from './aql'; import { @@ -866,8 +868,7 @@ handlers['secret-set'] = async function ({ name, value }) { }, ); } catch (error) { - console.error(error); - return { error: 'failed' }; + return { error: 'failed', reason: error.reason }; } }; @@ -1543,22 +1544,35 @@ handlers['subscribe-needs-bootstrap'] = async function ({ return { bootstrapped: res.data.bootstrapped, - loginMethod: res.data.loginMethod || 'password', + availableLoginMethods: res.data.availableLoginMethods || [ + { method: 'password', active: true, displayName: 'Password' }, + ], + multiuser: res.data.multiuser || false, hasServer: true, }; }; -handlers['subscribe-bootstrap'] = async function ({ password }) { +handlers['subscribe-bootstrap'] = async function (loginConfig) { + try { + await post(getServer().SIGNUP_SERVER + '/bootstrap', loginConfig); + } catch (err) { + return { error: err.reason || 'network-failure' }; + } + return {}; +}; + +handlers['subscribe-get-login-methods'] = async function () { let res; try { - res = await post(getServer().SIGNUP_SERVER + '/bootstrap', { password }); + res = await fetch(getServer().SIGNUP_SERVER + '/login-methods').then(res => + res.json(), + ); } catch (err) { return { error: err.reason || 'network-failure' }; } - if (res.token) { - await asyncStorage.setItem('user-token', res.token); - return {}; + if (res.methods) { + return { methods: res.methods }; } return { error: 'internal' }; }; @@ -1583,16 +1597,38 @@ handlers['subscribe-get-user'] = async function () { 'X-ACTUAL-TOKEN': userToken, }, }); - const { status, reason } = JSON.parse(res); + let tokenExpired = false; + const { + status, + reason, + data: { + userName = null, + permission = '', + userId = null, + displayName = null, + loginMethod = null, + } = {}, + } = JSON.parse(res) || {}; if (status === 'error') { if (reason === 'unauthorized') { return null; + } else if (reason === 'token-expired') { + tokenExpired = true; + } else { + return { offline: true }; } - return { offline: true }; } - return { offline: false }; + return { + offline: false, + userName, + permission, + userId, + displayName, + loginMethod, + tokenExpired, + }; } catch (e) { console.log(e); return { offline: true }; @@ -1617,21 +1653,25 @@ handlers['subscribe-change-password'] = async function ({ password }) { return {}; }; -handlers['subscribe-sign-in'] = async function ({ password, loginMethod }) { - if (typeof loginMethod !== 'string' || loginMethod == null) { - loginMethod = 'password'; +handlers['subscribe-sign-in'] = async function (loginInfo) { + if ( + typeof loginInfo.loginMethod !== 'string' || + loginInfo.loginMethod == null + ) { + loginInfo.loginMethod = 'password'; } let res; try { - res = await post(getServer().SIGNUP_SERVER + '/login', { - loginMethod, - password, - }); + res = await post(getServer().SIGNUP_SERVER + '/login', loginInfo); } catch (err) { return { error: err.reason || 'network-failure' }; } + if (res.redirect_url) { + return { redirect_url: res.redirect_url }; + } + if (!res.token) { throw new Error('login: User token not set'); } @@ -1651,6 +1691,10 @@ handlers['subscribe-sign-out'] = async function () { return 'ok'; }; +handlers['subscribe-set-token'] = async function ({ token }) { + await asyncStorage.setItem('user-token', token); +}; + handlers['get-server-version'] = async function () { if (!getServer()) { return { error: 'no-server' }; @@ -1735,6 +1779,7 @@ handlers['get-budgets'] = async function () { ? { encryptKeyId: prefs.encryptKeyId } : {}), ...(prefs.groupId ? { groupId: prefs.groupId } : {}), + ...(prefs.owner ? { owner: prefs.owner } : {}), name: prefs.budgetName || '(no name)', } satisfies Budget; } @@ -1752,6 +1797,10 @@ handlers['get-remote-files'] = async function () { return cloudStorage.listRemoteFiles(); }; +handlers['get-user-file-info'] = async function (fileId: string) { + return cloudStorage.getRemoteFile(fileId); +}; + handlers['reset-budget-cache'] = mutator(async function () { // Recomputing everything will update the cache await sheet.loadUserBudgets(db); @@ -2077,6 +2126,104 @@ handlers['export-budget'] = async function () { } }; +handlers['enable-openid'] = async function (loginConfig) { + try { + const userToken = await asyncStorage.getItem('user-token'); + + if (!userToken) { + return { error: 'unauthorized' }; + } + + await post(getServer().BASE_SERVER + '/openid/enable', loginConfig, { + 'X-ACTUAL-TOKEN': userToken, + }); + } catch (err) { + return { error: err.reason || 'network-failure' }; + } + return {}; +}; + +handlers['enable-password'] = async function (loginConfig) { + try { + const userToken = await asyncStorage.getItem('user-token'); + + if (!userToken) { + return { error: 'unauthorized' }; + } + + await post(getServer().BASE_SERVER + '/openid/disable', loginConfig, { + 'X-ACTUAL-TOKEN': userToken, + }); + } catch (err) { + return { error: err.reason || 'network-failure' }; + } + return {}; +}; + +handlers['get-openid-config'] = async function () { + try { + const res = await get(getServer().BASE_SERVER + '/openid/config'); + + if (res) { + const config = JSON.parse(res) as OpenIdConfig; + return { openId: config }; + } + + return null; + } catch (err) { + return { error: 'config-fetch-failed' }; + } +}; + +handlers['enable-openid'] = async function (loginConfig) { + try { + const userToken = await asyncStorage.getItem('user-token'); + + if (!userToken) { + return { error: 'unauthorized' }; + } + + await post(getServer().BASE_SERVER + '/openid/enable', loginConfig, { + 'X-ACTUAL-TOKEN': userToken, + }); + } catch (err) { + return { error: err.reason || 'network-failure' }; + } + return {}; +}; + +handlers['enable-password'] = async function (loginConfig) { + try { + const userToken = await asyncStorage.getItem('user-token'); + + if (!userToken) { + return { error: 'unauthorized' }; + } + + await post(getServer().BASE_SERVER + '/openid/disable', loginConfig, { + 'X-ACTUAL-TOKEN': userToken, + }); + } catch (err) { + return { error: err.reason || 'network-failure' }; + } + return {}; +}; + +handlers['get-openid-config'] = async function () { + try { + const res = await get(getServer().BASE_SERVER + '/openid/config'); + + if (res) { + const config = JSON.parse(res) as OpenIdConfig; + return { openId: config }; + } + + return null; + } catch (err) { + return { error: 'config-fetch-failed' }; + } +}; + async function loadBudget(id: string) { let dir: string; try { @@ -2265,6 +2412,7 @@ app.combine( filtersApp, reportsApp, rulesApp, + adminApp, ); function getDefaultDocumentDir() { diff --git a/packages/loot-core/src/server/post.ts b/packages/loot-core/src/server/post.ts index 1fa28cd22a0..b521d87a147 100644 --- a/packages/loot-core/src/server/post.ts +++ b/packages/loot-core/src/server/post.ts @@ -80,6 +80,102 @@ export async function post(url, data, headers = {}, timeout = null) { return res.data; } +export async function del(url, data, headers = {}, timeout = null) { + let text; + let res; + + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + const signal = timeout ? controller.signal : null; + res = await fetch(url, { + method: 'DELETE', + body: JSON.stringify(data), + signal, + headers: { + ...headers, + 'Content-Type': 'application/json', + }, + }); + clearTimeout(timeoutId); + text = await res.text(); + } catch (err) { + throw new PostError('network-failure'); + } + + throwIfNot200(res, text); + + try { + res = JSON.parse(text); + } catch (err) { + // Something seriously went wrong. TODO handle errors + throw new PostError('parse-json', { meta: text }); + } + + if (res.status !== 'ok') { + console.log( + 'API call failed: ' + + url + + '\nData: ' + + JSON.stringify(data, null, 2) + + '\nResponse: ' + + JSON.stringify(res, null, 2), + ); + + throw new PostError(res.description || res.reason || 'unknown'); + } + + return res.data; +} + +export async function patch(url, data, headers = {}, timeout = null) { + let text; + let res; + + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + const signal = timeout ? controller.signal : null; + res = await fetch(url, { + method: 'PATCH', + body: JSON.stringify(data), + signal, + headers: { + ...headers, + 'Content-Type': 'application/json', + }, + }); + clearTimeout(timeoutId); + text = await res.text(); + } catch (err) { + throw new PostError('network-failure'); + } + + throwIfNot200(res, text); + + try { + res = JSON.parse(text); + } catch (err) { + // Something seriously went wrong. TODO handle errors + throw new PostError('parse-json', { meta: text }); + } + + if (res.status !== 'ok') { + console.log( + 'API call failed: ' + + url + + '\nData: ' + + JSON.stringify(data, null, 2) + + '\nResponse: ' + + JSON.stringify(res, null, 2), + ); + + throw new PostError(res.description || res.reason || 'unknown'); + } + + return res.data; +} + export async function postBinary(url, data, headers) { let res; try { diff --git a/packages/loot-core/src/shared/errors.ts b/packages/loot-core/src/shared/errors.ts index 0dffe54f229..c9a69c937d6 100644 --- a/packages/loot-core/src/shared/errors.ts +++ b/packages/loot-core/src/shared/errors.ts @@ -123,3 +123,54 @@ export class LazyLoadFailedError extends Error { this.cause = cause; } } + +export function getUserAccessErrors(reason: string) { + switch (reason) { + case 'unauthorized': + return t('You are not logged in.'); + case 'token-expired': + return t('Login expired, please login again.'); + case 'user-cant-be-empty': + return t('Please select a user.'); + case 'invalid-file-id': + return t('This file is invalid.'); + case 'file-denied': + return t('You don`t have permissions over this file.'); + case 'user-already-have-access': + return t('User already has access.'); + default: + return t( + 'An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: {{reason}})', + { reason }, + ); + } +} + +export function getSecretsError(error: string, reason: string) { + switch (reason) { + case 'unauthorized': + return t('You are not logged in.'); + case 'not-admin': + return t('You have to be admin to set secrets'); + default: + return error; + } +} + +export function getOpenIdErrors(reason: string) { + switch (reason) { + case 'unauthorized': + return t('You are not logged in.'); + case 'configuration-error': + return t('This configuration is not valid. Please check it again.'); + case 'unable-to-change-file-config-enabled': + return t( + 'Unable to enable OpenID. Please update the config.json file in this case.', + ); + default: + return t( + 'An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: {{reason}})', + { reason }, + ); + } +} diff --git a/packages/loot-core/src/types/budget.d.ts b/packages/loot-core/src/types/budget.d.ts index 5d8e394fdb4..321ed41a69c 100644 --- a/packages/loot-core/src/types/budget.d.ts +++ b/packages/loot-core/src/types/budget.d.ts @@ -4,4 +4,5 @@ export type Budget = { encryptKeyId?: string; groupId?: string; name: string; + owner?: string; }; diff --git a/packages/loot-core/src/types/file.d.ts b/packages/loot-core/src/types/file.d.ts index e9db42f96fe..27bf6c051bb 100644 --- a/packages/loot-core/src/types/file.d.ts +++ b/packages/loot-core/src/types/file.d.ts @@ -1,3 +1,5 @@ +import { UsersWithAccess } from '../server/cloud-storage'; + import { Budget } from './budget'; export type FileState = @@ -18,6 +20,7 @@ export type SyncableLocalFile = Budget & { groupId: string; state: 'broken' | 'unknown'; hasKey: boolean; + owner: string; }; export type SyncedLocalFile = Budget & { @@ -26,6 +29,8 @@ export type SyncedLocalFile = Budget & { encryptKeyId?: string; hasKey: boolean; state: 'synced' | 'detached'; + owner: string; + usersWithAccess: UsersWithAccess[]; }; export type RemoteFile = { @@ -35,6 +40,8 @@ export type RemoteFile = { encryptKeyId?: string; hasKey: boolean; state: 'remote'; + owner: string; + usersWithAccess: UsersWithAccess[]; }; export type File = LocalFile | SyncableLocalFile | SyncedLocalFile | RemoteFile; diff --git a/packages/loot-core/src/types/handlers.d.ts b/packages/loot-core/src/types/handlers.d.ts index ef880e2515b..cf5b2c1bec8 100644 --- a/packages/loot-core/src/types/handlers.d.ts +++ b/packages/loot-core/src/types/handlers.d.ts @@ -1,3 +1,4 @@ +import type { AdminHandlers } from '../server/admin/types/handlers'; import type { BudgetHandlers } from '../server/budget/types/handlers'; import type { DashboardHandlers } from '../server/dashboard/types/handlers'; import type { FiltersHandlers } from '../server/filters/types/handlers'; @@ -22,6 +23,7 @@ export interface Handlers ReportsHandlers, RulesHandlers, SchedulesHandlers, + AdminHandlers, ToolsHandlers {} export type HandlerFunctions = Handlers[keyof Handlers]; diff --git a/packages/loot-core/src/types/models/index.d.ts b/packages/loot-core/src/types/models/index.d.ts index b4ba56346c7..543ca5eca11 100644 --- a/packages/loot-core/src/types/models/index.d.ts +++ b/packages/loot-core/src/types/models/index.d.ts @@ -12,3 +12,4 @@ export type * from './rule'; export type * from './schedule'; export type * from './transaction'; export type * from './transaction-filter'; +export type * from './user'; diff --git a/packages/loot-core/src/types/models/openid.d.ts b/packages/loot-core/src/types/models/openid.d.ts new file mode 100644 index 00000000000..3d357bedf10 --- /dev/null +++ b/packages/loot-core/src/types/models/openid.d.ts @@ -0,0 +1,7 @@ +export type OpenIdConfig = { + selectedProvider: string; + issuer: string; + client_id: string; + client_secret: string; + server_hostname: string; +}; diff --git a/packages/loot-core/src/types/models/user.ts b/packages/loot-core/src/types/models/user.ts new file mode 100644 index 00000000000..43b85030d0e --- /dev/null +++ b/packages/loot-core/src/types/models/user.ts @@ -0,0 +1,30 @@ +export interface NewUserEntity { + userName: string; + displayName: string; + role: string; + enabled: boolean; +} + +export interface UserEntity extends NewUserEntity { + id: string; + owner: boolean; +} + +export interface UserEntityDropdown { + userId: string; + userName: string; + displayName?: string; +} + +export interface UserAvailable { + userId: string; + displayName?: string; + userName: string; + haveAccess?: number; + owner?: number; +} + +export const PossibleRoles = { + ADMIN: 'Admin', + BASIC: 'Basic', +}; diff --git a/packages/loot-core/src/types/models/userAccess.ts b/packages/loot-core/src/types/models/userAccess.ts new file mode 100644 index 00000000000..a1fdb2508e5 --- /dev/null +++ b/packages/loot-core/src/types/models/userAccess.ts @@ -0,0 +1,10 @@ +export interface NewUserAccessEntity { + fileId: string; + userId: string; +} + +export interface UserAccessEntity extends NewUserAccessEntity { + displayName: string; + userName: string; + fileName: string; +} diff --git a/packages/loot-core/src/types/prefs.d.ts b/packages/loot-core/src/types/prefs.d.ts index 6c54362a90d..97610ce05d8 100644 --- a/packages/loot-core/src/types/prefs.d.ts +++ b/packages/loot-core/src/types/prefs.d.ts @@ -2,7 +2,8 @@ export type FeatureFlag = | 'goalTemplatesEnabled' | 'actionTemplating' | 'upcomingLengthAdjustment' - | 'contextMenus'; + | 'contextMenus' + | 'openidAuth'; /** * Cross-device preferences. These sync across devices when they are changed. @@ -81,3 +82,5 @@ export type GlobalPrefs = Partial<{ documentDir: string; // Electron only serverSelfSignedCert: string; // Electron only }>; + +export type AuthMethods = 'password' | 'openid'; diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts index 92b872e54f5..4c623262c00 100644 --- a/packages/loot-core/src/types/server-handlers.d.ts +++ b/packages/loot-core/src/types/server-handlers.d.ts @@ -17,6 +17,7 @@ import { RuleEntity, PayeeEntity, } from './models'; +import { OpenIdConfig } from './models/openid'; import { GlobalPrefs, MetadataPrefs } from './prefs'; import { Query } from './query'; import { EmptyObject } from './util'; @@ -269,27 +270,64 @@ export interface ServerHandlers { 'get-did-bootstrap': () => Promise<boolean>; - 'subscribe-needs-bootstrap': (args: { - url; - }) => Promise< - { error: string } | { bootstrapped: unknown; hasServer: boolean } + 'subscribe-needs-bootstrap': (args: { url }) => Promise< + | { error: string } + | { + bootstrapped: boolean; + hasServer: false; + } + | { + bootstrapped: boolean; + hasServer: true; + availableLoginMethods: { + method: string; + displayName: string; + active: boolean; + }[]; + multiuser: boolean; + } >; - 'subscribe-bootstrap': (arg: { password }) => Promise<{ error?: string }>; + 'subscribe-get-login-methods': () => Promise<{ + methods?: { method: string; displayName: string; active: boolean }[]; + error?: string; + }>; - 'subscribe-get-user': () => Promise<{ offline: boolean } | null>; + 'subscribe-bootstrap': (arg: { + password?: string; + openId?: OpenIdConfig; + }) => Promise<{ error?: string }>; + + 'subscribe-get-user': () => Promise<{ + offline: boolean; + userName?: string; + userId?: string; + displayName?: string; + permission?: string; + loginMethod?: string; + tokenExpired?: boolean; + } | null>; 'subscribe-change-password': (arg: { password; }) => Promise<{ error?: string }>; - 'subscribe-sign-in': (arg: { - password; - loginMethod?: string; - }) => Promise<{ error?: string }>; + 'subscribe-sign-in': ( + arg: + | { + password; + loginMethod?: string; + } + | { + return_url; + loginMethod?: 'openid'; + }, + ) => Promise<{ error?: string }>; 'subscribe-sign-out': () => Promise<'ok'>; + 'subscribe-set-token': (arg: { token: string }) => Promise<void>; + 'get-server-version': () => Promise<{ error?: string } | { version: string }>; 'get-server-url': () => Promise<string | null>; @@ -314,6 +352,8 @@ export interface ServerHandlers { 'get-remote-files': () => Promise<RemoteFile[]>; + 'get-user-file-info': (fileId: string) => Promise<RemoteFile | null>; + 'reset-budget-cache': () => Promise<unknown>; 'upload-budget': (arg: { id }) => Promise<{ error?: string }>; @@ -380,4 +420,18 @@ export interface ServerHandlers { 'get-last-opened-backup': () => Promise<string | null>; 'app-focused': () => Promise<void>; + + 'enable-openid': (arg: { + openId?: OpenIdConfig; + }) => Promise<{ error?: string }>; + + 'enable-password': (arg: { password: string }) => Promise<{ error?: string }>; + + 'get-openid-config': () => Promise< + | { + openId: OpenIdConfig; + } + | { error: string } + | null + >; } diff --git a/packages/loot-core/typings/window.d.ts b/packages/loot-core/typings/window.d.ts index 1a56a14fb20..3a63353e776 100644 --- a/packages/loot-core/typings/window.d.ts +++ b/packages/loot-core/typings/window.d.ts @@ -17,6 +17,7 @@ declare global { relaunch: () => void; reload: (() => Promise<void>) | undefined; restartElectronServer: () => void; + startOAuthServer: () => Promise<string>; moveBudgetDirectory: ( currentBudgetDirectory: string, newDirectory: string, diff --git a/upcoming-release-notes/3878.md b/upcoming-release-notes/3878.md new file mode 100644 index 00000000000..e1b8c807de7 --- /dev/null +++ b/upcoming-release-notes/3878.md @@ -0,0 +1,6 @@ +--- +category: Features +authors: [apilat, lelemm] +--- + +Add support for authentication using OpenID Connect.