mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
V2 Handle SSO account restrictions in account settings (#5225)
## Summary - hide password and username update controls for SSO accounts and show a managed-account notice - prevent account update handlers from calling APIs when the user authenticates via SSO - expose authenticationType on the user session model and add translations for new SSO messaging ## Testing - not run (not requested) ------ [Codex Task](https://chatgpt.com/codex/tasks/task_b_693ae8144148832888ecf128e66cd3ca)
This commit is contained in:
@@ -449,6 +449,7 @@ required = "All fields are required."
|
|||||||
mismatch = "New passwords do not match."
|
mismatch = "New passwords do not match."
|
||||||
error = "Unable to update password. Please verify your current password and try again."
|
error = "Unable to update password. Please verify your current password and try again."
|
||||||
success = "Password updated successfully. Please sign in again."
|
success = "Password updated successfully. Please sign in again."
|
||||||
|
ssoDisabled = "Password changes are managed by your identity provider."
|
||||||
current = "Current password"
|
current = "Current password"
|
||||||
currentPlaceholder = "Enter your current password"
|
currentPlaceholder = "Enter your current password"
|
||||||
new = "New password"
|
new = "New password"
|
||||||
@@ -510,6 +511,7 @@ low = "Low"
|
|||||||
title = "Change Credentials"
|
title = "Change Credentials"
|
||||||
header = "Update Your Account Details"
|
header = "Update Your Account Details"
|
||||||
changePassword = "You are using default login credentials. Please enter a new password"
|
changePassword = "You are using default login credentials. Please enter a new password"
|
||||||
|
ssoManaged = "Your account is managed by your identity provider."
|
||||||
newUsername = "New Username"
|
newUsername = "New Username"
|
||||||
oldPassword = "Current Password"
|
oldPassword = "Current Password"
|
||||||
newPassword = "New Password"
|
newPassword = "New Password"
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export interface User {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
is_anonymous?: boolean;
|
is_anonymous?: boolean;
|
||||||
isFirstLogin?: boolean;
|
isFirstLogin?: boolean;
|
||||||
|
authenticationType?: string;
|
||||||
app_metadata?: Record<string, any>;
|
app_metadata?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,17 @@ const AccountSection: React.FC = () => {
|
|||||||
const [usernameError, setUsernameError] = useState('');
|
const [usernameError, setUsernameError] = useState('');
|
||||||
const [usernameSubmitting, setUsernameSubmitting] = useState(false);
|
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 userIdentifier = useMemo(() => user?.email || user?.username || '', [user?.email, user?.username]);
|
||||||
|
|
||||||
const redirectToLogin = useCallback(() => {
|
const redirectToLogin = useCallback(() => {
|
||||||
@@ -41,6 +52,11 @@ const AccountSection: React.FC = () => {
|
|||||||
const handlePasswordSubmit = async (event: React.FormEvent) => {
|
const handlePasswordSubmit = async (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (isSsoUser) {
|
||||||
|
setPasswordError(t('settings.security.password.ssoDisabled', 'Password changes are managed by your identity provider.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentPassword || !newPassword || !confirmPassword) {
|
if (!currentPassword || !newPassword || !confirmPassword) {
|
||||||
setPasswordError(t('settings.security.password.required', 'All fields are required.'));
|
setPasswordError(t('settings.security.password.required', 'All fields are required.'));
|
||||||
return;
|
return;
|
||||||
@@ -81,6 +97,11 @@ const AccountSection: React.FC = () => {
|
|||||||
const handleUsernameSubmit = async (event: React.FormEvent) => {
|
const handleUsernameSubmit = async (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (isSsoUser) {
|
||||||
|
setUsernameError(t('changeCreds.ssoManaged', 'Your account is managed by your identity provider.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentPasswordForUsername || !newUsername) {
|
if (!currentPasswordForUsername || !newUsername) {
|
||||||
setUsernameError(t('settings.security.password.required', 'All fields are required.'));
|
setUsernameError(t('settings.security.password.required', 'All fields are required.'));
|
||||||
return;
|
return;
|
||||||
@@ -132,11 +153,21 @@ const AccountSection: React.FC = () => {
|
|||||||
: t('account.accountSettings', 'Account Settings')}
|
: t('account.accountSettings', 'Account Settings')}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
|
||||||
<Group gap="sm" wrap="wrap">
|
<Group gap="sm" wrap="wrap">
|
||||||
|
{!isSsoUser && (
|
||||||
<Button leftSection={<LocalIcon icon="key-rounded" />} onClick={() => setPasswordModalOpen(true)}>
|
<Button leftSection={<LocalIcon icon="key-rounded" />} onClick={() => setPasswordModalOpen(true)}>
|
||||||
{t('settings.security.password.update', 'Update password')}
|
{t('settings.security.password.update', 'Update password')}
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSsoUser && (
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<LocalIcon icon="edit-rounded" />}
|
leftSection={<LocalIcon icon="edit-rounded" />}
|
||||||
@@ -144,12 +175,14 @@ const AccountSection: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{t('account.changeUsername', 'Change username')}
|
{t('account.changeUsername', 'Change username')}
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button variant="outline" color="red" leftSection={<LocalIcon icon="logout-rounded" />} onClick={handleLogout}>
|
<Button variant="outline" color="red" leftSection={<LocalIcon icon="logout-rounded" />} onClick={handleLogout}>
|
||||||
{t('settings.general.logout', 'Log out')}
|
{t('settings.general.logout', 'Log out')}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
Reference in New Issue
Block a user