Merge remote-tracking branch 'origin/main' into pdfCache

This commit is contained in:
Anthony Stirling 2025-12-15 21:58:35 +00:00
commit 9c84e1eed4
3 changed files with 51 additions and 15 deletions

View File

@ -449,6 +449,7 @@ required = "All fields are required."
mismatch = "New passwords do not match."
error = "Unable to update password. Please verify your current password and try again."
success = "Password updated successfully. Please sign in again."
ssoDisabled = "Password changes are managed by your identity provider."
current = "Current password"
currentPlaceholder = "Enter your current password"
new = "New password"
@ -510,6 +511,7 @@ low = "Low"
title = "Change Credentials"
header = "Update Your Account Details"
changePassword = "You are using default login credentials. Please enter a new password"
ssoManaged = "Your account is managed by your identity provider."
newUsername = "New Username"
oldPassword = "Current Password"
newPassword = "New Password"

View File

@ -60,6 +60,7 @@ export interface User {
enabled?: boolean;
is_anonymous?: boolean;
isFirstLogin?: boolean;
authenticationType?: string;
app_metadata?: Record<string, any>;
}

View File

@ -24,6 +24,17 @@ const AccountSection: React.FC = () => {
const [usernameError, setUsernameError] = useState('');
const [usernameSubmitting, setUsernameSubmitting] = useState(false);
const authTypeFromMetadata = useMemo(() => {
const metadata = user?.app_metadata as { authType?: string; authenticationType?: string } | undefined;
return metadata?.authenticationType ?? metadata?.authType;
}, [user?.app_metadata]);
const normalizedAuthType = useMemo(
() => (user?.authenticationType ?? authTypeFromMetadata ?? '').toLowerCase(),
[authTypeFromMetadata, user?.authenticationType]
);
const isSsoUser = useMemo(() => ['sso', 'oauth2', 'saml2'].includes(normalizedAuthType), [normalizedAuthType]);
const userIdentifier = useMemo(() => user?.email || user?.username || '', [user?.email, user?.username]);
const redirectToLogin = useCallback(() => {
@ -41,6 +52,11 @@ const AccountSection: React.FC = () => {
const handlePasswordSubmit = async (event: React.FormEvent) => {
event.preventDefault();
if (isSsoUser) {
setPasswordError(t('settings.security.password.ssoDisabled', 'Password changes are managed by your identity provider.'));
return;
}
if (!currentPassword || !newPassword || !confirmPassword) {
setPasswordError(t('settings.security.password.required', 'All fields are required.'));
return;
@ -81,6 +97,11 @@ const AccountSection: React.FC = () => {
const handleUsernameSubmit = async (event: React.FormEvent) => {
event.preventDefault();
if (isSsoUser) {
setUsernameError(t('changeCreds.ssoManaged', 'Your account is managed by your identity provider.'));
return;
}
if (!currentPasswordForUsername || !newUsername) {
setUsernameError(t('settings.security.password.required', 'All fields are required.'));
return;
@ -132,23 +153,35 @@ const AccountSection: React.FC = () => {
: t('account.accountSettings', 'Account Settings')}
</Text>
<Group gap="sm" wrap="wrap">
<Button leftSection={<LocalIcon icon="key-rounded" />} onClick={() => setPasswordModalOpen(true)}>
{t('settings.security.password.update', 'Update password')}
</Button>
<Stack gap="xs">
{isSsoUser && (
<Alert icon={<LocalIcon icon="info" width="1rem" height="1rem" />} color="blue" variant="light">
{t('changeCreds.ssoManaged', 'Your account is managed by your identity provider.')}
</Alert>
)}
<Button
variant="light"
leftSection={<LocalIcon icon="edit-rounded" />}
onClick={() => setUsernameModalOpen(true)}
>
{t('account.changeUsername', 'Change username')}
</Button>
<Group gap="sm" wrap="wrap">
{!isSsoUser && (
<Button leftSection={<LocalIcon icon="key-rounded" />} onClick={() => setPasswordModalOpen(true)}>
{t('settings.security.password.update', 'Update password')}
</Button>
)}
<Button variant="outline" color="red" leftSection={<LocalIcon icon="logout-rounded" />} onClick={handleLogout}>
{t('settings.general.logout', 'Log out')}
</Button>
</Group>
{!isSsoUser && (
<Button
variant="light"
leftSection={<LocalIcon icon="edit-rounded" />}
onClick={() => setUsernameModalOpen(true)}
>
{t('account.changeUsername', 'Change username')}
</Button>
)}
<Button variant="outline" color="red" leftSection={<LocalIcon icon="logout-rounded" />} onClick={handleLogout}>
{t('settings.general.logout', 'Log out')}
</Button>
</Group>
</Stack>
</Stack>
</Paper>