From 1b55a16d79fa76269256dc270559edb727350d2b Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Fri, 22 Nov 2024 16:38:52 -0300 Subject: [PATCH] OpenId implementation --- packages/api/methods.ts | 5 +- ...ecks-the-page-visuals-1-chromium-linux.png | Bin 83023 -> 83165 bytes ...ecks-the-page-visuals-3-chromium-linux.png | Bin 80500 -> 80661 bytes .../desktop-client/src/auth/AuthProvider.tsx | 45 ++ .../src/auth/ProtectedRoute.tsx | 64 +++ packages/desktop-client/src/auth/types.ts | 3 + .../desktop-client/src/components/App.tsx | 42 +- .../src/components/FinancesApp.tsx | 31 +- .../src/components/LoggedInUser.tsx | 169 +++++-- .../src/components/ManageRules.tsx | 2 +- .../desktop-client/src/components/Modals.tsx | 41 ++ .../src/components/ServerContext.tsx | 74 +++- .../admin/UserAccess/UserAccess.tsx | 345 +++++++++++++++ .../admin/UserAccess/UserAccessHeader.tsx | 13 + .../admin/UserAccess/UserAccessPage.tsx | 19 + .../admin/UserAccess/UserAccessRow.tsx | 143 ++++++ .../admin/UserDirectory/UserDirectory.tsx | 340 ++++++++++++++ .../UserDirectory/UserDirectoryHeader.tsx | 31 ++ .../admin/UserDirectory/UserDirectoryPage.tsx | 46 ++ .../admin/UserDirectory/UserDirectoryRow.tsx | 143 ++++++ .../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 | 255 +++++++++-- .../src/components/manager/ManagementApp.tsx | 42 +- .../manager/subscribe/Bootstrap.tsx | 22 +- .../manager/subscribe/ConfirmPasswordForm.tsx | 67 ++- .../components/manager/subscribe/Login.tsx | 330 +++++++++----- .../manager/subscribe/OpenIdCallback.ts | 16 + .../manager/subscribe/OpenIdForm.tsx | 416 ++++++++++++++++++ .../components/manager/subscribe/common.tsx | 30 +- .../components/modals/CreateAccountModal.tsx | 300 +++++++------ .../src/components/modals/EditAccess.tsx | 151 +++++++ .../src/components/modals/EditUser.tsx | 338 ++++++++++++++ .../modals/GoCardlessInitialiseModal.tsx | 44 +- .../components/modals/OpenIDEnableModal.tsx | 94 ++++ .../components/modals/PasswordEnableModal.tsx | 133 ++++++ .../modals/SimpleFinInitialiseModal.tsx | 20 +- .../components/modals/TransferOwnership.tsx | 166 +++++++ .../src/components/responsive/wide.ts | 2 + .../src/components/settings/AuthSettings.tsx | 93 ++++ .../src/components/settings/Experimental.tsx | 6 + .../src/components/settings/index.tsx | 2 + .../transactions/TransactionsTable.test.jsx | 53 +-- .../src/hooks/useFeatureFlag.ts | 1 + .../src/hooks/useSyncServerStatus.ts | 2 +- packages/desktop-client/src/icons/v1/Role.tsx | 20 + packages/desktop-client/src/icons/v1/role.svg | 8 + packages/desktop-client/src/index.tsx | 5 +- packages/desktop-client/vite.config.mts | 7 + .../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 | 241 ++++++++++ .../src/server/admin/types/handlers.ts | 53 +++ packages/loot-core/src/server/api-models.ts | 2 + .../loot-core/src/server/cloud-storage.ts | 46 ++ packages/loot-core/src/server/main.ts | 130 +++++- packages/loot-core/src/server/post.ts | 96 ++++ packages/loot-core/src/shared/errors.ts | 43 ++ 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 | 70 ++- upcoming-release-notes/2945.md | 6 + upcoming-release-notes/3878.md | 6 + 72 files changed, 4619 insertions(+), 406 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/desktop-client/src/icons/v1/Role.tsx create mode 100644 packages/desktop-client/src/icons/v1/role.svg 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/2945.md 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 6edcc155e8ccffed081beb7d5500e1fb49217efb..358471ece2fba3cf9deba54bfa222e615e241c37 100644 GIT binary patch literal 83165 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 literal 83023 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
7@W5 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

!BK4nkPf61%Vz4Xe61=|v*WkE>rnhrd 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 literal 80500 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�Ktr-LuxrTyxDevly>6A-SSXEbL$Z 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 DCk!yN diff --git a/packages/desktop-client/src/auth/AuthProvider.tsx b/packages/desktop-client/src/auth/AuthProvider.tsx new file mode 100644 index 00000000000..cfeea44af3b --- /dev/null +++ b/packages/desktop-client/src/auth/AuthProvider.tsx @@ -0,0 +1,45 @@ +import React, { createContext, useContext, type ReactNode } from 'react'; +import { useSelector } from 'react-redux'; + +import { type State } from 'loot-core/client/state-types'; + +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 hasPermission = (permission?: Permissions) => { + if (!permission) { + return true; + } + + return ( + (userData?.offline ?? false) || + 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..73c58fd547c --- /dev/null +++ b/packages/desktop-client/src/auth/ProtectedRoute.tsx @@ -0,0 +1,64 @@ +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.state === 'remote' || f.state === 'synced' || f.state === 'detached', + ) as (SyncedLocalFile | RemoteFile)[]; + 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/components/App.tsx b/packages/desktop-client/src/components/App.tsx index 4c5895c52eb..e5f3338e9e5 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,42 @@ function AppInner() { global.Actual.updateAppMenu(budgetId); }, [budgetId]); + useEffect(() => { + if (userData?.tokenExpired) { + addNotification({ + type: 'error', + id: 'login-expired', + title: 'Login expired', + sticky: true, + message: 'Login expired, please login again.', + button: { + title: 'Go to login', + action: () => { + signOut(); + }, + }, + }); + } + }, [userData, userData?.tokenExpired]); + + useEffect(() => { + if (userData?.tokenExpired) { + addNotification({ + type: 'error', + id: 'login-expired', + title: 'Login expired', + sticky: true, + message: 'Login expired, please login again.', + button: { + title: '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 ec7df311bbf..2aa1adaa42c 100644 --- a/packages/desktop-client/src/components/LoggedInUser.tsx +++ b/packages/desktop-client/src/components/LoggedInUser.tsx @@ -1,11 +1,16 @@ -// @ts-strict-ignore import React, { useState, useEffect, useRef, type CSSProperties } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; 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 { useActions } from '../hooks/useActions'; +import { useMetadataPref } from '../hooks/useMetadataPref'; +import { useNavigate } from '../hooks/useNavigate'; import { theme, styles } from '../style'; import { Button } from './common/Button2'; @@ -13,13 +18,14 @@ 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 { useMultiuserEnabled, useServerURL } from './ServerContext'; type LoggedInUserProps = { hideIfNoServer?: boolean; style?: CSSProperties; color?: string; }; + export function LoggedInUser({ hideIfNoServer, style, @@ -32,38 +38,84 @@ 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 navigate = useNavigate(); + const [budgetId] = useMetadataPref('id'); + const [cloudFileId] = useMetadataPref('cloudFileId'); + const location = useLocation(); + const { hasPermission } = useAuth(); + const [isOwner, setIsOwner] = useState(false); + 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); useEffect(() => { - getUserData().then(() => setLoading(false)); - }, []); + if (getUserData) { + getUserData().then(() => setLoading(false)); + } + }, [getUserData]); - async function onChangePassword() { - await closeBudget(); - window.__navigate('/change-password'); - } + useEffect(() => { + if (cloudFileId && currentFile) { + setIsOwner( + currentFile.usersWithAccess.some( + u => u.userId === userData?.userId && u.owner, + ), + ); + } else { + setIsOwner(false); + } + }, [cloudFileId]); - async function onMenuSelect(type) { + const handleMenuSelect = async (type: string) => { setMenuOpen(false); switch (type) { case 'change-password': - onChangePassword(); + await closeBudget(); + if (window.__navigate) { + window.__navigate('/change-password'); + } else { + window.location.href = '/change-password'; + } break; case 'sign-in': await closeBudget(); - window.__navigate('/login'); + if (window.__navigate) { + window.__navigate('/login'); + } else { + window.location.href = '/login'; + } + break; + case 'user-access': + navigate('/user-access'); + break; + case 'user-directory': + navigate('/user-directory'); + break; + case 'index': + navigate('/'); break; case 'sign-out': - signOut(); + if (signOut) { + signOut(); + } break; case 'config-server': await closeBudget(); - window.__navigate('/config-server'); + if (window.__navigate) { + window.__navigate('/config-server'); + } else { + window.location.href = '/config-server'; + } break; default: + break; } - } + }; function serverMessage() { if (!serverUrl) { @@ -77,9 +129,7 @@ export function LoggedInUser({ return t('Server online'); } - if (hideIfNoServer && !serverUrl) { - return null; - } + if (hideIfNoServer && !serverUrl) return null; if (loading && serverUrl) { return ( @@ -96,17 +146,81 @@ 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 && + (isOwner || 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?.userName && ( + + (logged 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 a52f6fef71d..10423f5a643 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'; @@ -53,7 +55,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'; @@ -64,6 +68,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'; @@ -604,9 +609,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..dd59c14cec4 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, @@ -9,25 +8,60 @@ import React, { } from 'react'; 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 +71,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 +96,26 @@ export function ServerProvider({ children }: { children: ReactNode }) { run(); }, []); + const refreshLoginMethods = useCallback(async () => { + if (serverURL) { + const data = await send('subscribe-get-login-methods'); + setAvailableLoginMethods(data.methods); + } + }, [serverURL]); + + useEffect(() => { + if (serverURL) { + send('subscribe-needs-bootstrap').then( + (data: Awaited>) => { + if ('hasServer' in data && data.hasServer) { + setAvailableLoginMethods(data.loginMethods); + setMultiuserEnabled(data.multiuser); + } + }, + ); + } + }, [serverURL]); + const setURL = useCallback( async (url: string, opts: { validate?: boolean } = {}) => { const { error } = await send('set-server-url', { ...opts, url }); @@ -65,8 +132,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..1bb1bbcc56f --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx @@ -0,0 +1,345 @@ +// @ts-strict-ignore +import React, { + useState, + useEffect, + useCallback, + useMemo, + type SetStateAction, + type Dispatch, + useRef, +} from 'react'; +import { useDispatch } from 'react-redux'; + +import { css } from '@emotion/css'; + +import { 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 { SvgDotsHorizontalTriple, SvgLockOpen } from '../../../icons/v1'; +import { SvgLockClosed } from '../../../icons/v2'; +import { styles, theme } from '../../../style'; +import { Button } from '../../common/Button2'; +import { Link } from '../../common/Link'; +import { Popover } from '../../common/Popover'; +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 [allAccess, setAllAccess] = useState([]); + const [page, setPage] = useState(0); + const [filter, setFilter] = useState(''); + const [cloudFileId] = useMetadataPref('cloudFileId'); + const dispatch = useDispatch(); + const [ownerName, setOwnerName] = useState('unknown'); + const triggerRef = useRef(null); + const [menuOpen, setMenuOpen] = useState(false); + + 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 users: UserAvailable[] = 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 ?? ''); + }; + + const loadedAccess = users + .map(user => ({ + ...user, + displayName: user.displayName || user.userName, + })) + .sort(sortUsers); + + setAllAccess(loadedAccess); + return loadedAccess; + }, [cloudFileId, setLoading]); + + const loadOwner = useCallback(async () => { + debugger; + 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(); + const owner = await loadOwner(); + if (owner) { + setOwnerName(owner?.displayName ?? owner?.userName); + } + } 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 + + + + + + + + + setMenuOpen(false)} + style={{ padding: 10 }} + > + + + Owner: + + + {ownerName} + + + + + + + + + + + + + + ); +} + +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 ( + + ); + })} + + ); +} + +const wrapperStyle = css({ + display: 'inline-block', + ':hover .default-icon': { + display: 'none', + }, + ':hover .hover-icon': { + display: 'inline', + }, +}); + +const iconStyle = css({ + '&.hover-icon': { + display: 'none', + }, +}); + +const LockToggle = props => ( +
+
+ +
+
+ +
+
+); 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..4459daf6623 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccessHeader.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { Cell, TableHeader } from '../../table'; + +export function UserAccessHeader() { + 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..63c965c1a61 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccessPage.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { Page } from '../../Page'; + +import { UserAccess } from './UserAccess'; + +export function UserAccessPage() { + 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..d8c089d83c8 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccessRow.tsx @@ -0,0 +1,143 @@ +// @ts-strict-ignore +import React, { memo, useState } from 'react'; + +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 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: 'Access Revocation Incomplete', + message: '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: 'Login expired', + sticky: true, + message: getUserAccessErrors(error), + button: { + title: 'Go to login', + action: () => { + actions.signOut(); + }, + }, + }); + } else { + actions.addNotification({ + type: 'error', + title: '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..fb860546b2f --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx @@ -0,0 +1,340 @@ +// @ts-strict-ignore +import React, { + useState, + useEffect, + useCallback, + useMemo, + type SetStateAction, + type Dispatch, +} from 'react'; +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 getUserDirectoryErrors(reason) { + switch (reason) { + case 'unauthorized': + return 'You are not logged in.'; + case 'token-expired': + return 'Login expired, please login again.'; + case 'user-cant-be-empty': + return 'Please enter a value for the username; the field cannot be empty.'; + case 'role-cant-be-empty': + return 'Select a role; the field cannot be empty.'; + case 'user-already-exists': + return 'The username you entered already exists. Please choose a different username.'; + case 'not-all-deleted': + return 'Not all users were deleted. Check if one of the selected users is the server owner.'; + case 'role-does-not-exists': + return 'Selected role does not exists, possibly a bug? Visit https://actualbudget.org/contact/ for support.'; + default: + return `An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: ${reason})`; + } +} + +function UserDirectoryContent({ + isModal, + setLoading, +}: ManageUserDirectoryContentProps) { + const [allUsers, setAllUsers] = useState([]); + const [page, setPage] = useState(0); + const [filter, setFilter] = useState(''); + const dispatch = useDispatch(); + const actions = useActions(); + + 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: 'Login expired', + sticky: true, + message: getUserDirectoryErrors(error), + button: { + title: 'Go to login', + action: () => actions.signOut(), + }, + }); + } else { + actions.addNotification({ + type: 'error', + title: 'Something happened while deleting users', + sticky: true, + message: getUserDirectoryErrors(error), + }); + } + } + + await loadUsers(); + selectedInst.dispatch({ type: 'select-none' }); + setLoading(false); + }, [actions, loadUsers, selectedInst, setLoading]); + + 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 && ( + + )} + + + + + + ); +} + +function EmptyMessage({ text, style }) { + 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..8377c67d0f4 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryHeader.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { + useSelectedItems, + useSelectedDispatch, +} from '../../../hooks/useSelected'; +import { SelectCell, Cell, TableHeader } from '../../table'; + +export function UserDirectoryHeader() { + 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..396ed0102c1 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryPage.tsx @@ -0,0 +1,46 @@ +import React, { type ReactNode } from 'react'; + +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; +}) { + 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..f9b6f40ee54 --- /dev/null +++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectoryRow.tsx @@ -0,0 +1,143 @@ +// @ts-strict-ignore +import React, { memo } from 'react'; + +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 72a3ebab0c0..dfc2105cbef 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, @@ -114,54 +129,113 @@ function FileMenuButton({ onDelete }: { onDelete: () => void }) { ); } -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; 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 = multiuserEnabled ? getOwnerDisplayName() : ''; break; case 'local': + Icon = SvgFileDouble; + status = 'Local'; + break; case 'broken': + ownerName = 'unknown'; Icon = SvgFileDouble; status = t('Local'); break; default: Icon = SvgCloudCheck; status = t('Syncing'); + ownerName = multiuserEnabled ? getOwnerDisplayName() : ''; break; } + const showOwnerContent = multiuserEnabled && file.owner !== currentUserId; + return ( - - + + > + + + {status} + - {status} + + {showOwnerContent && ( + + + Owner: + + + {ownerName} + + + )} + ); + + function getOwnerDisplayName() { + if ( + !( + file.state === 'remote' || + file.state === 'synced' || + file.state === 'detached' + ) + ) { + return ''; + } + + const userFound = file.usersWithAccess?.find(f => f.owner); + return userFound?.displayName ?? userFound?.userName ?? 'unknown'; + } } function FileItem({ @@ -169,13 +243,16 @@ function FileItem({ quickSwitchMode, onSelect, onDelete, + currentUserId, }: { file: File; quickSwitchMode: boolean; onSelect: (file: File) => void; onDelete: (file: File) => void; + currentUserId: string; }) { const { t } = useTranslation(); + const multiuserEnabled = useMultiuserEnabled(); const selecting = useRef(false); @@ -211,11 +288,19 @@ function FileItem({ > - {file.name} - - + + {file.name} + {multiuserEnabled && ( + + )} + + + void; onDelete: (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( @@ -397,6 +498,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; @@ -444,6 +546,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { return ( dispatch(pushModal('delete-budget', { file }))} @@ -520,3 +624,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..ef8b9c0cfc6 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 'OpenID server cannot be empty'; + case 'missing-client-id': + return 'Client ID cannot be empty'; + case 'missing-client-secret': + return '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..d1fc093275b 100644 --- a/packages/desktop-client/src/components/manager/subscribe/ConfirmPasswordForm.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/ConfirmPasswordForm.tsx @@ -1,5 +1,5 @@ // @ts-strict-ignore -import React, { useState } from 'react'; +import React, { type ChangeEvent, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { ButtonWithLoading } from '../../common/Button2'; @@ -83,3 +83,68 @@ 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); + + 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="Password" + type={showPassword ? 'text' : 'password'} + value={password} + onChange={(e: ChangeEvent<HTMLInputElement>) => + setPassword(e.target.value) + } + onEnter={onSubmit} + /> + + <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..d95c7c1e7a9 100644 --- a/packages/desktop-client/src/components/manager/subscribe/Login.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/Login.tsx @@ -2,44 +2,213 @@ 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 { 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); + + 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="Password" + type="password" + onChangeValue={newValue => setPassword(newValue)} + style={{ flex: 1, marginRight: 10 }} + onEnter={onSubmitPassword} + /> + <ButtonWithLoading + variant="primary" + isLoading={loading} + style={{ fontSize: 15 }} + onPress={onSubmitPassword} + > + Sign in + </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: window.location.origin, + loginMethod: 'openid', + }); + + if (error) { + setError(error); + } else { + window.location.href = redirect_url; + } + } + + return ( + <View> + {!reviewOpenIdConfiguration && ( + <> + <View style={{ flexDirection: 'row' }}> + <Button + variant="primary" + style={{ + fontSize: 15, + alignSelf: 'center', + flexGrow: 1, + marginTop: 5, + }} + onPress={onSubmitOpenId} + > + Sign in with OpenID + </Button> + </View> + {warnMasterCreation && ( + <> + <label style={{ color: theme.warningText, marginTop: 10 }}> + The first user to login with OpenID will be the{' '} + <Text style={{ fontWeight: 'bold' }}>server owner</Text>. This + can't be changed using UI. + </label> + <Button + variant="bare" + onPress={() => setReviewOpenIdConfiguration(true)} + style={{ marginTop: 5 }} + > + Review OpenID configuration + </Button> + </> + )} + </> + )} + {reviewOpenIdConfiguration && ( + <OpenIdForm + loadData={true} + otherButtons={[ + <Button + key="cancel" + variant="bare" + style={{ marginRight: 10 }} + onPress={() => setReviewOpenIdConfiguration(false)} + > + Cancel + </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} + > + Login with Password + </Link> + ) : ( + <span> + Checking Header Token Login ...{' '} + <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,7 +218,7 @@ export function Login() { } })(); } - }, [checked, searchParams, method, dispatch]); + }, [loginMethods, checked, searchParams, method, dispatch]); function getErrorMessage(error) { switch (error) { @@ -61,35 +230,13 @@ export function Login() { return 'Invalid password'; case 'network-failure': return 'Unable to contact the server'; + case 'internal-error': + return 'Internal error'; default: return `An unknown error occurred: ${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 +244,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="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 +296,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..6de5644ccf1 --- /dev/null +++ b/packages/desktop-client/src/components/manager/subscribe/OpenIdForm.tsx @@ -0,0 +1,416 @@ +import { type ReactNode, useEffect, useState } from 'react'; +import { useLocation, type Location } from 'react-router-dom'; + +import { send } from 'loot-core/platform/client/fetch'; +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 [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: { openId?: OpenIdConfig }) => { + setProviderName(config?.openId?.selectedProvider ?? 'other'); + setIssuer(config?.openId?.issuer ?? ''); + setClientId(config?.openId?.client_id ?? ''); + setClientSecret(config?.openId?.client_secret ?? ''); + }); + } + }, [loadData]); + + 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 && 'The OpenID provider URL.'}{' '} + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + {tip} + </Text> + </label>{' '} + <Stack> + <FormField style={{ flex: 1 }}> + <FormLabel title="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, + }} + > + The Client ID generated by the OpenID provider. + </label> + </FormField> + <FormField style={{ flex: 1 }}> + <FormLabel title="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, + }} + > + The client secret associated with the ID generated by the OpenID + provider. + </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" + > + Integrating Google Sign-In into your web app + </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/"> + Get started with passwordless.id + </Link> + ), + }, + { + label: 'Microsoft Entra', + value: 'microsoft', + issuer: 'https://login.microsoftonline.com/common/v2.0/', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Link + variant="external" + to="https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc" + > + OpenID Connect on the Microsoft identity platform + </Link> + ), + }, + { + label: 'Auth0', + value: 'auth0', + issuer: 'https://{domain.region}.auth0.com/', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Text style={{ color: theme.warningText }}> + Note that the URL depends on your application domain and region.{' '} + <Link + variant="external" + to="https://auth0.com/docs/get-started/applications/application-settings" + > + Auth0 application settings + </Link> + </Text> + ), + }, + { + label: 'Keycloak', + value: 'keycloak', + issuer: 'https://{domain}/realms/{realm}/', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: false, + tip: ( + <Text style={{ color: theme.warningText }}> + Note that the URL depends on your Keycloak domain and realm.{' '} + <Link + variant="external" + to="https://www.keycloak.org/docs/22.0.0/securing_apps/" + > + Securing Applications with Keycloak + </Link> + </Text> + ), + }, + { + label: 'Github', + value: 'github', + clientIdRequired: true, + clientSecretRequired: true, + clientSecretDisabled: true, + clientIdDisabled: true, + submitButtonDisabled: true, + tip: ( + <> + <Text style={{ color: theme.errorText }}> + Github does not support discovery. You need to configure it in the + server. + </Text>{' '} + <Link + variant="external" + to="https://actualbudget.org/docs/" + linkColor="muted" + > + Learn more + </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 }}> + Note that the URL depends on your Authentik domain and provider slug + name.{' '} + <Link + variant="external" + to="https://docs.goauthentik.io/docs/providers/oauth2/" + > + Configure OAuth2 Provider + </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> + 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>{' '} + <Link + variant="external" + to="https://actualbudget.org/docs/" + linkColor="muted" + > + Learn more + </Link> + </Text> + ), + }, +]; + +function OpenIdProviderSelector({ + onProviderChange, + defaultValue, +}: { + onProviderChange: OnProviderChangeCallback; + defaultValue: string; +}) { + 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="OpenID Provider" htmlFor="provider-selector" /> + <Select + options={openIdProviders.map(provider => + provider === Menu.line ? Menu.line : [provider.value, provider.label], + )} + defaultLabel="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..d20638347ef 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.loginMethods); + 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.loginMethods); + } } else { ensure('/bootstrap'); } diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx index 8f62089c913..58458156c9d 100644 --- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx @@ -6,12 +6,15 @@ 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; @@ -37,6 +41,8 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { const [menuGoCardlessOpen, setGoCardlessMenuOpen] = useState<boolean>(false); const triggerRef = useRef(null); const [menuSimplefinOpen, setSimplefinMenuOpen] = useState<boolean>(false); + const { hasPermission } = useAuth(); + const multiuserEnabled = useMultiuserEnabled(); const onConnectGoCardless = () => { if (!isGoCardlessSetupComplete) { @@ -181,6 +187,9 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { title = t('Link Account'); } + const canSetSecrets = + !multiuserEnabled || hasPermission(Permissions.ADMINISTRATOR); + return ( <Modal name="add-account"> {({ state: { close } }) => ( @@ -226,141 +235,168 @@ 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 && ( - <> - <Button - ref={triggerRef} - variant="bare" - onPress={() => setGoCardlessMenuOpen(true)} - aria-label="GoCardless menu" - > - <SvgDotsHorizontalTriple - width={15} - height={15} - style={{ transform: 'rotateZ(90deg)' }} - /> - </Button> - - <Popover - triggerRef={triggerRef} - isOpen={menuGoCardlessOpen} - onOpenChange={() => setGoCardlessMenuOpen(false)} - > - <Menu - onMenuSelect={item => { - if (item === 'reconfigure') { - onGoCardlessReset(); - } - }} - items={[ - { - name: 'reconfigure', - text: t('Reset GoCardless credentials'), - }, - ]} - /> - </Popover> - </> - )} - </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 && ( - <> - <Button - ref={triggerRef} - variant="bare" - onPress={() => setSimplefinMenuOpen(true)} - aria-label="SimpleFIN menu" + {(canSetSecrets || isGoCardlessSetupComplete) && ( + <> + <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> - <Popover - triggerRef={triggerRef} - isOpen={menuSimplefinOpen} - onOpenChange={() => setSimplefinMenuOpen(false)} + {isGoCardlessSetupComplete + ? t('Link bank account with GoCardless') + : t('Set up GoCardless for bank sync')} + </ButtonWithLoading> + {isGoCardlessSetupComplete && canSetSecrets && ( + <> + <Button + ref={triggerRef} + variant="bare" + onPress={() => setGoCardlessMenuOpen(true)} + aria-label="GoCardless menu" + > + <SvgDotsHorizontalTriple + width={15} + height={15} + style={{ transform: 'rotateZ(90deg)' }} + /> + </Button> + {menuGoCardlessOpen && ( + <Popover + triggerRef={triggerRef} + isOpen={menuGoCardlessOpen} + onOpenChange={() => + setGoCardlessMenuOpen(false) + } + > + <Menu + onMenuSelect={item => { + if (item === 'reconfigure') { + onGoCardlessReset(); + } + }} + items={[ + { + name: 'reconfigure', + text: t('Reset GoCardless credentials'), + }, + ]} + /> + </Popover> + )} + </> + )} + </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> + </> + )} + {(canSetSecrets || isSimpleFinSetupComplete) && ( + <> + <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} > - <Menu - onMenuSelect={item => { - if (item === 'reconfigure') { - onSimpleFinReset(); - } - }} - items={[ - { - name: 'reconfigure', - text: t('Reset SimpleFIN credentials'), - }, - ]} - /> - </Popover> - </> + {isSimpleFinSetupComplete + ? t('Link bank account with SimpleFIN') + : t('Set up SimpleFIN for bank sync')} + </ButtonWithLoading> + {isSimpleFinSetupComplete && canSetSecrets && ( + <> + <Button + ref={triggerRef} + variant="bare" + onPress={() => setSimplefinMenuOpen(true)} + aria-label="SimpleFIN menu" + > + <SvgDotsHorizontalTriple + width={15} + height={15} + style={{ transform: 'rotateZ(90deg)' }} + /> + </Button> + {menuSimplefinOpen && ( + <Popover + triggerRef={triggerRef} + isOpen={menuSimplefinOpen} + onOpenChange={() => setSimplefinMenuOpen(false)} + > + <Menu + onMenuSelect={item => { + if (item === 'reconfigure') { + onSimpleFinReset(); + } + }} + items={[ + { + name: 'reconfigure', + text: t('Reset SimpleFIN credentials'), + }, + ]} + /> + </Popover> + )} + </> + )} + </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> + You don't have the required permissions to set up + secrets. Please contact an Admin to configure{' '} + {[ + isGoCardlessSetupComplete ? '' : 'GoCardless', + isSimpleFinSetupComplete ? '' : 'SimpleFin', + ] + .filter(Boolean) // Remove empty values + .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..169f0f0f0ef --- /dev/null +++ b/packages/desktop-client/src/components/modals/EditAccess.tsx @@ -0,0 +1,151 @@ +import { useEffect, useState } from 'react'; + +import { send } from 'loot-core/platform/client/fetch'; +import { getUserAccessErrors } from 'loot-core/shared/errors'; +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 UserAvailable = { + userId: string; + displayName?: string; + userName: string; +}; + +type EditUserAccessProps = { + defaultUserAccess: UserAccessEntity; + onSave?: (userAccess: UserAccessEntity) => void; +}; + +export function EditUserAccess({ + defaultUserAccess, + onSave: originalOnSave, +}: EditUserAccessProps) { + 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( + (users: UserAvailable[]) => + setAvailableUsers( + users.map(user => [ + user.userId, + user.displayName + ? `${user.displayName} (${user.userName})` + : user.userName, + ]), + ), + ); + }, [defaultUserAccess.fileId]); + + 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: 'Login expired', + sticky: true, + message: getUserAccessErrors(error), + button: { + title: 'Go to login', + action: () => { + actions.signOut(); + }, + }, + }); + } else { + setSetError(getUserAccessErrors(error)); + } + } + } + + return ( + <Modal name="edit-access"> + {({ state: { close } }: { state: { close: () => void } }) => ( + <> + <ModalHeader + title="User Access" + rightContent={<ModalCloseButton onPress={close} />} + /> + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title="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, + }} + > + Select a user from the directory + </label> + </View> + )} + {availableUsers.length === 0 && ( + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + No users available to give access + </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 ? 'Save' : '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..fa08078fb2e --- /dev/null +++ b/packages/desktop-client/src/components/modals/EditUser.tsx @@ -0,0 +1,338 @@ +import { useState } from 'react'; + +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 getUserDirectoryErrors(reason: string): string { + switch (reason) { + case 'unauthorized': + return 'You are not logged in.'; + case 'token-expired': + return 'Login expired, please login again.'; + case 'user-cant-be-empty': + return 'Please enter a value for the username; the field cannot be empty.'; + case 'role-cant-be-empty': + return 'Select a role; the field cannot be empty.'; + case 'user-already-exists': + return 'The username you entered already exists. Please choose a different username.'; + case 'not-all-deleted': + return 'Not all users were deleted. Check if one of the selected users is the server owner.'; + case 'role-does-not-exists': + return 'Selected role does not exists, possibly a bug? Visit https://actualbudget.org/contact/ for support.'; + default: + return `An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: ${reason})`; + } +} + +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: 'Login expired', + sticky: true, + message: getUserDirectoryErrors(error), + button: { + title: 'Go to login', + action: () => { + actions.signOut(); + }, + }, + }); + } + + return false; + } + + return true; +} + +export function EditUserFinanceApp({ + defaultUser, + onSave: originalOnSave, +}: EditUserFinanceAppProps) { + return ( + <Modal name="edit-user"> + {({ state: { close } }) => ( + <> + <ModalHeader + title="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 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() { + 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="Username" htmlFor="name-field" /> + <Input value={userName} onChangeValue={text => setUserName(text)} /> + <label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + The username registered within the OpenID provider. + </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.warningTextLight, + marginTop: 5, + }} + > + Change this username with caution; it is the server owner. + </label> + )} + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title="Display Name" htmlFor="dispalyname-field" /> + <Input + value={displayName} + onChangeValue={text => setDisplayName(text)} + placeholder="(Optional)" + /> + <View + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + If left empty, it will be updated from your OpenID provider on the + user's login, if available there. + </View> + <View + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + When displaying user information, this will be shown instead of the + username. + </View> + </FormField> + </Stack> + <Stack direction="row" style={{ marginTop: 10, width: '100px' }}> + <FormField style={{ flex: 1 }}> + <FormLabel title="Role" htmlFor="name-field" /> + <Select + disabled={defaultUser.owner} + options={Object.entries(PossibleRoles)} + value={role} + onChange={newValue => setRole(newValue)} + /> + </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} + > + Cancel + </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, + }} + > + In our user directory, each user is assigned a specific role that + determines their permissions and capabilities within the system. + </Text> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + Understanding these roles is essential for managing users and + responsibilities effectively. + </Text> + <View style={{ paddingTop: 5 }}> + <label + style={{ + ...styles.altMenuHeaderText, + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + Basic + </label> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + Users with the Basic role can create new budgets and be invited to + collaborate on budgets created by others. + </Text> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + This role is ideal for users who primarily need to manage their own + budgets and participate in shared budget activities. + </Text> + </View> + <View style={{ paddingTop: 10 }}> + <label + style={{ + ...styles.altMenuHeaderText, + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + Admin + </label> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + 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. + </Text> + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + > + Also can assign ownership of a budget to another person, ensuring + efficient budget management. + </Text> + </View> + </View> + ); +}; diff --git a/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx b/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx index 188b5d57331..cd862085be0 100644 --- a/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx +++ b/packages/desktop-client/src/components/modals/GoCardlessInitialiseModal.tsx @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import { t } from 'i18next'; // Ensure this import is correct +import { getSecretsError } from 'loot-core/shared/errors'; import { send } from 'loot-core/src/platform/client/fetch'; import { Error } from '../alerts'; @@ -31,26 +32,45 @@ export const GoCardlessInitialiseModal = ({ const [secretKey, setSecretKey] = useState(''); const [isValid, setIsValid] = useState(true); const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState( + 'It is required to provide both the secret id and secret key.', + ); const onSubmit = async (close: () => void) => { if (!secretId || !secretKey) { setIsValid(false); + setError('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 +127,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..fae315be3c8 --- /dev/null +++ b/packages/desktop-client/src/components/modals/OpenIDEnableModal.tsx @@ -0,0 +1,94 @@ +import { useState } from 'react'; + +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 [error, setError] = useState(''); + const actions = useActions(); + const { closeBudget } = useActions(); + const refreshLoginMethods = useRefreshLoginMethods(); + + async function onSave(config: OpenIdConfig) { + const { error } = (await send('enable-openid', { openId: config })) || {}; + if (!error) { + originalOnSave?.(); + await refreshLoginMethods(); + await asyncStorage.removeItem('user-token'); + await closeBudget(); + } else { + setError(getOpenIdErrors(error)); + } + } + + return ( + <Modal name="enable-openid"> + {({ state: { close } }) => ( + <> + <ModalHeader + title="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} + > + Cancel + </Button>, + ]} + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + paddingTop: 5, + }} + title="After enabling openid all sessions will be closed" + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + }} + title="The first user to login will become the server owner" + /> + <Label + style={{ + ...styles.verySmallText, + color: theme.warningText, + }} + title="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..35ef2c872ee --- /dev/null +++ b/packages/desktop-client/src/components/modals/PasswordEnableModal.tsx @@ -0,0 +1,133 @@ +import { useState } from 'react'; + +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 [error, setError] = useState<string | null>(null); + const { closeBudget, popModal } = useActions(); + const multiuserEnabled = useMultiuserEnabled(); + const availableLoginMethods = useAvailableLoginMethods(); + const refreshLoginMethods = useRefreshLoginMethods(); + + const errorMessages = { + 'invalid-password': 'Invalid Password', + 'password-match': 'Passwords do not match', + 'network-failure': 'Unable to contact the server', + 'unable-to-change-file-config-enabled': + '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] || + '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="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()} + > + Cancel + </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()} + > + Cancel + </Button> + } + onSetPassword={onSetPassword} + /> + )} + </FormField> + <Label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + paddingTop: 5, + }} + title="After disabling openid all sessions will be closed" + /> + {multiuserEnabled && ( + <Label + style={{ + ...styles.verySmallText, + color: theme.warningText, + }} + title="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..920a657db2f 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('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..86a83e22e09 --- /dev/null +++ b/packages/desktop-client/src/components/modals/TransferOwnership.tsx @@ -0,0 +1,166 @@ +import { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +import { 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 UserEntity } from 'loot-core/types/models'; + +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 userData = useSelector((state: State) => state.user.data); + const actions = useActions(); + const [userId, setUserId] = useState(''); + const [error, setSetError] = 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(); + + useEffect(() => { + send('users-get').then((users: UserEntity[]) => + setAvailableUsers( + users + .filter(f => currentFile?.owner !== f.id) + .map(user => [ + user.id, + user.displayName + ? `${user.displayName} (${user.userName})` + : user.userName, + ]), + ), + ); + }, [userData?.userId, currentFile?.owner]); + + async function onSave() { + if (cloudFileId) { + const response = await send('transfer-ownership', { + fileId: cloudFileId as string, + newUserId: userId, + }); + const { error } = response || {}; + if (!error) { + originalOnSave?.(); + } else { + setSetError(getUserAccessErrors(error)); + } + } else { + setSetError('Cloud file ID is missing.'); + } + } + + return ( + <Modal name="transfer-ownership"> + {({ state: { close } }: { state: { close: () => void } }) => ( + <> + <ModalHeader + title="Transfer ownership" + rightContent={<ModalCloseButton onPress={close} />} + /> + <Stack direction="row" style={{ marginTop: 10 }}> + <FormField style={{ flex: 1 }}> + <FormLabel title="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, + }} + > + Select a user from the directory to designate as the new + budget owner. + </label> + <label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + This action is irreversible. Only the new owner or an + administrator can reverse it. + </label> + <label + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + Proceed with caution. + </label> + </View> + )} + {availableUsers.length === 0 && ( + <Text + style={{ + ...styles.verySmallText, + color: theme.pageTextLight, + marginTop: 5, + }} + > + 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}> + Cancel + </Button> + <Button + isDisabled={availableUsers.length === 0} + onPress={async () => { + await onSave(); + close(); + + await dispatch(closeAndLoadBudget((currentFile as Budget).id)); + }} + > + 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..7f1307334cf --- /dev/null +++ b/packages/desktop-client/src/components/settings/AuthSettings.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +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 { View } from '../common/View'; +import { useMultiuserEnabled, useLoginMethod } from '../ServerContext'; + +import { Setting } from './UI'; + +export function AuthSettings() { + const multiuserEnabled = useMultiuserEnabled(); + const loginMethod = useLoginMethod(); + const dispatch = useDispatch(); + const openidAuthFeatureFlag = useFeatureFlag('openidAuth'); + + return ( + <View style={{ display: openidAuthFeatureFlag ? 'visible' : 'none' }}> + {openidAuthFeatureFlag && ( + <Setting + primaryAction={ + <> + <label> + OpenID is{' '} + <label style={{ fontWeight: 'bold' }}> + {loginMethod === 'openid' ? 'enabled' : '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="OpenID is required to enable multi-user mode." + /> + </> + )} + {loginMethod !== 'password' && ( + <> + <Button + style={{ + marginTop: '10px', + }} + variant="normal" + onPress={() => + dispatch( + pushModal('enable-password-auth', { + onSave: async () => {}, + }), + ) + } + > + Disable OpenID + </Button> + {multiuserEnabled && ( + <label style={{ paddingTop: 5, color: theme.warningText }}> + Disabling OpenID will deactivate multi-user mode. + </label> + )} + </> + )} + </> + } + > + <Text> + <strong>Authentication method</strong> modifies how users log in to + the system. + </Text> + </Setting> + )} + </View> + ); +} 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 4a7852e12b0..c03887abad9 100644 --- a/packages/desktop-client/src/components/settings/index.tsx +++ b/packages/desktop-client/src/components/settings/index.tsx @@ -23,6 +23,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'; @@ -175,6 +176,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 95c73607e91..753bbe2f40d 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,31 +149,33 @@ 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} - /> - </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} + /> + </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/icons/v1/Role.tsx b/packages/desktop-client/src/icons/v1/Role.tsx new file mode 100644 index 00000000000..444c8a20fa4 --- /dev/null +++ b/packages/desktop-client/src/icons/v1/Role.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +export const SvgRole = (props: SVGProps<SVGSVGElement>) => ( + <svg + {...props} + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + style={{ + color: 'inherit', + ...props.style, + }} + > + <g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <circle cx="12" cy="8" r="4"/> + <path d="M5.5 21a8.5 8.5 0 0 1 13 0"/> + <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/> + <circle cx="12" cy="12" r="3"/> + </g> + </svg> +); diff --git a/packages/desktop-client/src/icons/v1/role.svg b/packages/desktop-client/src/icons/v1/role.svg new file mode 100644 index 00000000000..344454889cd --- /dev/null +++ b/packages/desktop-client/src/icons/v1/role.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> + <g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <circle cx="12" cy="8" r="4"/> + <path d="M5.5 21a8.5 8.5 0 0 1 13 0"/> + <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/> + <circle cx="12" cy="12" r="3"/> + </g> +</svg> 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/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 11450cf4775..b0bfc8ab46b 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; @@ -281,6 +283,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..0eee9f0212e --- /dev/null +++ b/packages/loot-core/src/server/admin/app.ts @@ -0,0 +1,241 @@ +// @ts-strict-ignore +import * as asyncStorage from '../../platform/server/asyncStorage'; +import { UserAvailable, UserEntity } from '../../types/models/user'; +import { UserAccessEntity } from '../../types/models/userAccess'; +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', { + token: userToken, + ids, + }); + + 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) { + const list = JSON.parse(res) as UserEntity[]; + return list; + } + } + + return null; +}); + +app.method('users-get-access', async function (fileIds) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + const res = await post( + getServer().BASE_SERVER + '/admin/access/get-bulk', + fileIds, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); + + if (res) { + return new Map<string, UserAccessEntity[]>(res); + } + } + + 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-get', async function (fileId) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + const res = await get( + `${getServer().BASE_SERVER + '/admin/access/'}?fileId=${fileId}`, + { + headers: { + 'X-ACTUAL-TOKEN': userToken, + }, + }, + ); + + if (res) { + return JSON.parse(res) as UserAccessEntity[]; + } + } + + return []; +}); + +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) { + return JSON.parse(res) as UserAvailable[]; + } + } + + 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('file-owner-get', async function (fileId) { + const userToken = await asyncStorage.getItem('user-token'); + + if (userToken) { + const res = await get( + `${getServer().BASE_SERVER + '/admin/file/owner'}?fileId=${fileId}`, + { + headers: { + 'X-ACTUAL-TOKEN': userToken, + }, + }, + ); + + if (res) { + return JSON.parse(res) as UserEntity; + } + } + + return null; +}); + +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..8f84cd30d94 --- /dev/null +++ b/packages/loot-core/src/server/admin/types/handlers.ts @@ -0,0 +1,53 @@ +import { UserAvailable, UserEntity } from '../../../types/models/user'; +import { + NewUserAccessEntity, + UserAccessEntity, +} from '../../../types/models/userAccess'; + +export interface AdminHandlers { + 'users-get': () => Promise<UserEntity[] | null>; + + 'users-get-access': ( + fileIds: string[], + ) => Promise<Map<string, UserAccessEntity[]> | null>; + + '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': (fileId: string) => Promise<UserAccessEntity[]>; + + 'access-get-available-users': (fileId: string) => Promise<UserAvailable[]>; + + 'transfer-ownership': ({ + fileId, + newUserId, + }: { + fileId: string; + newUserId: string; + }) => Promise<{ error?: string } | Record<string, never>>; + + 'file-owner-get': (fileId: string) => Promise<UserEntity | null>; + + '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..48a367c925e 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,6 +35,8 @@ export interface RemoteFile { name: string; encryptKeyId: string; hasKey: boolean; + owner: string; + usersWithAccess: UsersWithAccess[]; } async function checkHTTPStatus(res) { @@ -36,6 +44,12 @@ async function checkHTTPStatus(res) { return res.text().then(str => { throw new HTTPError(res.status, str); }); + } else if (res.status === 403) { + const data = JSON.parse(res.text())?.data; + if (data && data.reason === 'token-expired') { + asyncStorage.removeItem('user-token'); + window.location.href = '/'; + } } else { return res; } @@ -375,6 +389,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 aadde848273..3228c301996 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 { @@ -876,8 +878,7 @@ handlers['secret-set'] = async function ({ name, value }) { }, ); } catch (error) { - console.error(error); - return { error: 'failed' }; + return { error: 'failed', reason: error.reason }; } }; @@ -1553,22 +1554,35 @@ handlers['subscribe-needs-bootstrap'] = async function ({ return { bootstrapped: res.data.bootstrapped, - loginMethod: res.data.loginMethod || 'password', + loginMethods: res.data.loginMethods || [ + { 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' }; }; @@ -1593,16 +1607,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 }; @@ -1627,21 +1663,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'); } @@ -1661,6 +1701,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' }; @@ -1737,6 +1781,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; } @@ -1754,6 +1799,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); @@ -1993,6 +2042,50 @@ 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 () { + const res = await get(getServer().BASE_SERVER + '/openid/config'); + + if (res) { + return JSON.parse(res) as OpenIdConfig; + } + + return {}; +}; + async function loadBudget(id) { let dir; try { @@ -2181,6 +2274,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..7af3079e89f 100644 --- a/packages/loot-core/src/shared/errors.ts +++ b/packages/loot-core/src/shared/errors.ts @@ -123,3 +123,46 @@ export class LazyLoadFailedError extends Error { this.cause = cause; } } + +export function getUserAccessErrors(reason: string) { + switch (reason) { + case 'unauthorized': + return 'You are not logged in.'; + case 'token-expired': + return 'Login expired, please login again.'; + case 'user-cant-be-empty': + return 'Please select a user.'; + case 'invalid-file-id': + return 'This file is invalid.'; + case 'file-denied': + return `You don't have permissions over this file.`; + case 'user-already-have-access': + return `User already have access.`; + default: + return `An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: ${reason})`; + } +} + +export function getSecretsError(error: string, reason: string) { + switch (reason) { + case 'unauthorized': + return 'You are not logged in.'; + case 'not-admin': + return 'You have to be admin to set secrets'; + default: + return error; + } +} + +export function getOpenIdErrors(reason: string) { + switch (reason) { + case 'unauthorized': + return 'You are not logged in.'; + case 'configuration-error': + return 'This configuration is not valid. Please check it again.'; + case 'unable-to-change-file-config-enabled': + return 'Unable to enable OpenID. Please update the config.json file in this case.'; + default: + return `An internal error occurred, sorry! Visit https://actualbudget.org/contact/ for support. (ref: ${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 4afbc72a3e8..bba1a78026c 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; + loginMethods: { + 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>; @@ -308,6 +346,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 }>; @@ -357,4 +397,14 @@ 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; + }>; } diff --git a/upcoming-release-notes/2945.md b/upcoming-release-notes/2945.md new file mode 100644 index 00000000000..e1b8c807de7 --- /dev/null +++ b/upcoming-release-notes/2945.md @@ -0,0 +1,6 @@ +--- +category: Features +authors: [apilat, lelemm] +--- + +Add support for authentication using OpenID Connect. diff --git a/upcoming-release-notes/3878.md b/upcoming-release-notes/3878.md new file mode 100644 index 00000000000..f5c6d2d2b26 --- /dev/null +++ b/upcoming-release-notes/3878.md @@ -0,0 +1,6 @@ +--- +category: Features +authors: [apilat, lelemm] +--- + +Openid authentication