From cda45f9bfb0298daf8a6d8d586168f9d5520aa97 Mon Sep 17 00:00:00 2001 From: Sam Holton Date: Thu, 14 Mar 2024 17:33:39 -0400 Subject: [PATCH] feat(web): randomize password on reest (#7943) * feat(web): randomize password on reest * prettier * chore: clean up --------- Co-authored-by: Jason Rasmussen --- .../elements/buttons/link-button.svelte | 3 +- .../components/forms/edit-user-form.svelte | 22 ++++++- .../routes/admin/user-management/+page.svelte | 62 ++++++++++++------- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/web/src/lib/components/elements/buttons/link-button.svelte b/web/src/lib/components/elements/buttons/link-button.svelte index 2cb22d41da..6911fd2fc5 100644 --- a/web/src/lib/components/elements/buttons/link-button.svelte +++ b/web/src/lib/components/elements/buttons/link-button.svelte @@ -8,8 +8,9 @@ export let color: Color = 'transparent-gray'; export let disabled = false; export let fullwidth = false; + export let title: string | undefined = undefined; - diff --git a/web/src/lib/components/forms/edit-user-form.svelte b/web/src/lib/components/forms/edit-user-form.svelte index 13a797a9c8..cdd25c8d68 100644 --- a/web/src/lib/components/forms/edit-user-form.svelte +++ b/web/src/lib/components/forms/edit-user-form.svelte @@ -13,6 +13,7 @@ export let user: UserResponseDto; export let canResetPassword = true; + export let newPassword: string; let error: string; let success: string; @@ -53,12 +54,12 @@ const resetPassword = async () => { try { - const defaultPassword = 'password'; + newPassword = generatePassword(); await updateUser({ updateUserDto: { id: user.id, - password: defaultPassword, + password: newPassword, shouldChangePassword: true, }, }); @@ -70,6 +71,23 @@ isShowResetPasswordConfirmation = false; } }; + + // TODO move password reset server-side + function generatePassword(length: number = 16) { + let generatedPassword = ''; + + const characterSet = '0123456789' + 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + ',.-{}+!#$%/()=?'; + + for (let i = 0; i < length; i++) { + let randomNumber = crypto.getRandomValues(new Uint32Array(1))[0]; + randomNumber = randomNumber / 2 ** 32; + randomNumber = Math.floor(randomNumber * characterSet.length); + + generatedPassword += characterSet[randomNumber]; + } + + return generatedPassword; + }
import { page } from '$app/stores'; + import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import DeleteConfirmDialog from '$lib/components/admin-page/delete-confirm-dialoge.svelte'; + import LinkButton from '$lib/components/elements/buttons/link-button.svelte'; import RestoreDialogue from '$lib/components/admin-page/restore-dialoge.svelte'; import Button from '$lib/components/elements/buttons/button.svelte'; import Icon from '$lib/components/elements/icon.svelte'; @@ -17,8 +19,9 @@ import { user } from '$lib/stores/user.store'; import { websocketEvents } from '$lib/stores/websocket'; import { asByteUnitString } from '$lib/utils/byte-units'; + import { copyToClipboard } from '$lib/utils'; import { UserStatus, getAllUsers, type UserResponseDto } from '@immich/sdk'; - import { mdiClose, mdiDeleteRestore, mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js'; + import { mdiClose, mdiContentCopy, mdiDeleteRestore, mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js'; import { DateTime } from 'luxon'; import { onMount } from 'svelte'; import type { PageData } from './$types'; @@ -28,10 +31,11 @@ let allUsers: UserResponseDto[] = []; let shouldShowEditUserForm = false; let shouldShowCreateUserForm = false; - let shouldShowInfoPanel = false; + let shouldShowPasswordResetSuccess = false; let shouldShowDeleteConfirmDialog = false; let shouldShowRestoreDialog = false; let selectedUser: UserResponseDto; + let newPassword: string; const refresh = async () => { allUsers = await getAllUsers({ isAll: false }); @@ -84,7 +88,7 @@ const onEditPasswordSuccess = async () => { await refresh(); shouldShowEditUserForm = false; - shouldShowInfoPanel = true; + shouldShowPasswordResetSuccess = true; }; const deleteUserHandler = (user: UserResponseDto) => { @@ -121,6 +125,7 @@ (shouldShowEditUserForm = false)}> {/if} - {#if shouldShowInfoPanel} - (shouldShowInfoPanel = false)}> -
(shouldShowPasswordResetSuccess = false)}> + (shouldShowPasswordResetSuccess = false)} + onClose={() => (shouldShowPasswordResetSuccess = false)} + hideCancelButton={true} + confirmColor="green" > -

- Password reset success -

+ +
+

The user's password has been reset:

-

- The user's password has been reset to the default password -
-
- Please inform the user, and they will need to change the password at the next log-on. -

+
+ + {newPassword} + + copyToClipboard(newPassword)} title="Copy password"> +
+ +
+
+
-
- -
-
+

+ Please provide the temporary password to the user and inform them they will need to change the + password at their next login. +

+
+ +
{/if}