diff --git a/src/app/pages/me.vue b/src/app/pages/me.vue index b0bb54a7..69c2640d 100644 --- a/src/app/pages/me.vue +++ b/src/app/pages/me.vue @@ -27,6 +27,7 @@ {{ $t('general.password') }} @@ -125,6 +130,7 @@ const authStore = useAuthStore(); const name = ref(authStore.userData?.name); const email = ref(authStore.userData?.email); +const hasPassword = computed(() => authStore.userData?.hasPassword); const _submit = useSubmit( (data) => @@ -158,13 +164,14 @@ const _updatePassword = useSubmit( currentPassword.value = ''; newPassword.value = ''; confirmPassword.value = ''; + return authStore.update(); }, } ); function updatePassword() { return _updatePassword({ - currentPassword: currentPassword.value, + currentPassword: hasPassword.value ? currentPassword.value : null, newPassword: newPassword.value, confirmPassword: confirmPassword.value, }); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b8f2bc6e..48d631a9 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -28,6 +28,7 @@ "password": "Password", "newPassword": "New Password", "updatePassword": "Update Password", + "addPassword": "Add Password", "mtu": "MTU", "allowedIps": "Allowed IPs", "dns": "DNS", diff --git a/src/server/api/session.get.ts b/src/server/api/session.get.ts index 5d975ee5..32771f79 100644 --- a/src/server/api/session.get.ts +++ b/src/server/api/session.get.ts @@ -26,5 +26,6 @@ export default defineEventHandler(async (event) => { name: user.name, email: user.email, totpVerified: user.totpVerified, + hasPassword: user.password !== null, } satisfies SharedPublicUser; }); diff --git a/src/server/database/repositories/user/service.ts b/src/server/database/repositories/user/service.ts index ea4b4877..555f5ffd 100644 --- a/src/server/database/repositories/user/service.ts +++ b/src/server/database/repositories/user/service.ts @@ -193,7 +193,11 @@ export class UserService { return this.#statements.update.execute({ id, name, email }); } - async updatePassword(id: ID, currentPassword: string, newPassword: string) { + async updatePassword( + id: ID, + currentPassword: string | null, + newPassword: string + ) { const hash = await hashPassword(newPassword); return this.#db.transaction(async (tx) => { @@ -206,13 +210,20 @@ export class UserService { throw new Error('User not found'); } - const passwordValid = await isPasswordValid( - currentPassword, - txUser.password - ); + // only check password if already set + if (txUser.password !== null) { + if (!currentPassword) { + throw new Error('Invalid password'); + } - if (!passwordValid) { - throw new Error('Invalid password'); + const passwordValid = await isPasswordValid( + currentPassword, + txUser.password + ); + + if (!passwordValid) { + throw new Error('Invalid password'); + } } await tx diff --git a/src/server/database/repositories/user/types.ts b/src/server/database/repositories/user/types.ts index 4743be73..a5a31d04 100644 --- a/src/server/database/repositories/user/types.ts +++ b/src/server/database/repositories/user/types.ts @@ -57,7 +57,7 @@ export const UserUpdateSchema = z.object({ export const UserUpdatePasswordSchema = z .object({ - currentPassword: password, + currentPassword: password.nullable(), newPassword: password, confirmPassword: password, }) diff --git a/src/shared/utils/permissions.ts b/src/shared/utils/permissions.ts index 12fd5570..7a14855c 100644 --- a/src/shared/utils/permissions.ts +++ b/src/shared/utils/permissions.ts @@ -48,7 +48,7 @@ type SharedUserType = export type SharedPublicUser = Pick< UserType, 'id' | 'username' | 'name' | 'email' | 'totpVerified' -> & { role: BrandedNumber }; +> & { role: BrandedNumber; hasPassword: boolean }; type PermissionCheck = | boolean