Feat: Hash metrics password (#1778)
hash the metrics password if it is not already hashed
This commit is contained in:
@@ -135,7 +135,7 @@
|
||||
"sessionTimeoutDesc": "Session duration for Remember Me (seconds)",
|
||||
"metrics": "Metrics",
|
||||
"metricsPassword": "Password",
|
||||
"metricsPasswordDesc": "Bearer Password for the metrics endpoint (argon2 hash)",
|
||||
"metricsPasswordDesc": "Bearer Password for the metrics endpoint (password or argon2 hash)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "Route for metrics in JSON format",
|
||||
"prometheus": "Prometheus",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@libsql/client": "^0.15.1",
|
||||
"@nuxtjs/i18n": "^9.4.0",
|
||||
"@nuxtjs/tailwindcss": "^6.13.2",
|
||||
"@phc/format": "^1.0.0",
|
||||
"@pinia/nuxt": "^0.10.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"apexcharts": "^4.5.0",
|
||||
@@ -51,6 +52,7 @@
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint": "1.3.0",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/phc__format": "^1.0.1",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/semver": "^7.7.0",
|
||||
"drizzle-kit": "^0.30.6",
|
||||
|
||||
29
src/pnpm-lock.yaml
generated
29
src/pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
||||
'@nuxtjs/tailwindcss':
|
||||
specifier: ^6.13.2
|
||||
version: 6.13.2(magicast@0.3.5)
|
||||
'@phc/format':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@pinia/nuxt':
|
||||
specifier: ^0.10.1
|
||||
version: 0.10.1(magicast@0.3.5)(pinia@3.0.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))
|
||||
@@ -102,6 +105,9 @@ importers:
|
||||
'@types/debug':
|
||||
specifier: ^4.1.12
|
||||
version: 4.1.12
|
||||
'@types/phc__format':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
'@types/qrcode':
|
||||
specifier: ^1.5.5
|
||||
version: 1.5.5
|
||||
@@ -1602,11 +1608,11 @@ packages:
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
|
||||
|
||||
'@tanstack/virtual-core@3.13.5':
|
||||
resolution: {integrity: sha512-gMLNylxhJdUlfRR1G3U9rtuwUh2IjdrrniJIDcekVJN3/3i+bluvdMi3+eodnxzJq5nKnxnigo9h0lIpaqV6HQ==}
|
||||
'@tanstack/virtual-core@3.13.6':
|
||||
resolution: {integrity: sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==}
|
||||
|
||||
'@tanstack/vue-virtual@3.13.5':
|
||||
resolution: {integrity: sha512-1hhUA6CUjmKc5JDyKLcYOV6mI631FaKKxXh77Ja4UtIy6EOofYaLPk8vVgvK6vLMUSfHR2vI3ZpPY9ibyX60SA==}
|
||||
'@tanstack/vue-virtual@3.13.6':
|
||||
resolution: {integrity: sha512-GYdZ3SJBQPzgxhuCE2fvpiH46qzHiVx5XzBSdtESgiqh4poj8UgckjGWYEhxaBbcVt1oLzh1m3Ql4TyH32TOzQ==}
|
||||
peerDependencies:
|
||||
vue: ^2.7.0 || ^3.0.0
|
||||
|
||||
@@ -1641,6 +1647,9 @@ packages:
|
||||
'@types/parse-path@7.0.3':
|
||||
resolution: {integrity: sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==}
|
||||
|
||||
'@types/phc__format@1.0.1':
|
||||
resolution: {integrity: sha512-hoAQFKcP3voXk/ZEl3jrvS63o/HYLszq4nA2mqjytaSEHEy3j3t0gSFtPLnfKtX34k/xfath7etOoGw5ukoqXQ==}
|
||||
|
||||
'@types/qrcode@1.5.5':
|
||||
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
|
||||
|
||||
@@ -6664,11 +6673,11 @@ snapshots:
|
||||
mini-svg-data-uri: 1.4.4
|
||||
tailwindcss: 3.4.17
|
||||
|
||||
'@tanstack/virtual-core@3.13.5': {}
|
||||
'@tanstack/virtual-core@3.13.6': {}
|
||||
|
||||
'@tanstack/vue-virtual@3.13.5(vue@3.5.13(typescript@5.8.2))':
|
||||
'@tanstack/vue-virtual@3.13.6(vue@3.5.13(typescript@5.8.2))':
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.13.5
|
||||
'@tanstack/virtual-core': 3.13.6
|
||||
vue: 3.5.13(typescript@5.8.2)
|
||||
|
||||
'@trysound/sax@0.2.0': {}
|
||||
@@ -6698,6 +6707,10 @@ snapshots:
|
||||
|
||||
'@types/parse-path@7.0.3': {}
|
||||
|
||||
'@types/phc__format@1.0.1':
|
||||
dependencies:
|
||||
'@types/node': 22.13.14
|
||||
|
||||
'@types/qrcode@1.5.5':
|
||||
dependencies:
|
||||
'@types/node': 22.13.14
|
||||
@@ -9509,7 +9522,7 @@ snapshots:
|
||||
'@floating-ui/vue': 1.1.6(vue@3.5.13(typescript@5.8.2))
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/number': 3.6.0
|
||||
'@tanstack/vue-virtual': 3.13.5(vue@3.5.13(typescript@5.8.2))
|
||||
'@tanstack/vue-virtual': 3.13.6(vue@3.5.13(typescript@5.8.2))
|
||||
'@vueuse/core': 10.11.1(vue@3.5.13(typescript@5.8.2))
|
||||
'@vueuse/shared': 10.11.1(vue@3.5.13(typescript@5.8.2))
|
||||
aria-hidden: 1.2.4
|
||||
|
||||
@@ -107,7 +107,15 @@ export class GeneralService {
|
||||
};
|
||||
}
|
||||
|
||||
update(data: GeneralUpdateType) {
|
||||
async update(data: GeneralUpdateType) {
|
||||
// only hash the password if it is not already hashed
|
||||
if (
|
||||
data.metricsPassword !== null &&
|
||||
!isValidPasswordHash(data.metricsPassword)
|
||||
) {
|
||||
data.metricsPassword = await hashPassword(data.metricsPassword);
|
||||
}
|
||||
|
||||
return this.#db.update(general).set(data).execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ const metricsEnabled = z.boolean({ message: t('zod.general.metricsEnabled') });
|
||||
const metricsPassword = z
|
||||
.string({ message: t('zod.general.metricsPassword') })
|
||||
.min(1, { message: t('zod.general.metricsPassword') })
|
||||
// TODO?: validate argon2 regex
|
||||
.nullable();
|
||||
|
||||
export const GeneralUpdateSchema = z.object({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import argon2 from 'argon2';
|
||||
import { deserialize } from '@phc/format';
|
||||
|
||||
/**
|
||||
* Checks if `password` matches the hash.
|
||||
@@ -16,3 +17,21 @@ export function isPasswordValid(
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return argon2.hash(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the password hash is valid.
|
||||
* This only checks if the hash is a valid PHC formatted string using argon2.
|
||||
*/
|
||||
export function isValidPasswordHash(hash: string): boolean {
|
||||
try {
|
||||
const obj = deserialize(hash);
|
||||
|
||||
if (obj.id !== 'argon2i' && obj.id !== 'argon2d' && obj.id !== 'argon2id') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user