Add a user pref to reveal dotted passwords - Closes #208

This commit is contained in:
Bubka 2023-12-05 11:40:39 +01:00
parent c75e4eb047
commit b2e733eee5
7 changed files with 35 additions and 7 deletions

View File

@ -83,6 +83,7 @@
'preferences' => [ 'preferences' => [
'showOtpAsDot' => false, 'showOtpAsDot' => false,
'revealDottedOTP' => false,
'closeOtpOnCopy' => false, 'closeOtpOnCopy' => false,
'copyOtpOnDisplay' => false, 'copyOtpOnDisplay' => false,
'useBasicQrcodeReader' => false, 'useBasicQrcodeReader' => false,

View File

@ -48,6 +48,7 @@
const generated_at = ref(null) const generated_at = ref(null)
const hasTOTP = ref(false) const hasTOTP = ref(false)
const showInlineSpinner = ref(false) const showInlineSpinner = ref(false)
const revealPassword = ref(false)
const dots = ref() const dots = ref()
const totpLooper = ref() const totpLooper = ref()
@ -66,6 +67,7 @@
* *
*/ */
const show = async (accountId) => { const show = async (accountId) => {
revealPassword.value = false
// 3 possible cases : // 3 possible cases :
// //
@ -225,6 +227,7 @@
} }
else if(user.preferences.closeOtpOnCopy && (permit_closing || false) === true) { else if(user.preferences.closeOtpOnCopy && (permit_closing || false) === true) {
emit("please-close-me"); emit("please-close-me");
revealPassword.value = false
clearOTP() clearOTP()
} }
@ -285,7 +288,7 @@
@keyup.enter="copyOTP(password, true)" @keyup.enter="copyOTP(password, true)"
:title="$t('commons.copy_to_clipboard')" :title="$t('commons.copy_to_clipboard')"
> >
{{ useDisplayablePassword(password) }} {{ useDisplayablePassword(password, user.preferences.showOtpAsDot && user.preferences.revealDottedOTP && revealPassword) }}
</span> </span>
<span v-else tabindex="0" class="otp is-size-1"> <span v-else tabindex="0" class="otp is-size-1">
<Spinner :isVisible="showInlineSpinner" :type="'raw'" /> <Spinner :isVisible="showInlineSpinner" :type="'raw'" />
@ -293,9 +296,15 @@
</p> </p>
</UseColorMode> </UseColorMode>
<Dots v-show="isTimeBased(otpauthParams.otp_type)" ref="dots"></Dots> <Dots v-show="isTimeBased(otpauthParams.otp_type)" ref="dots"></Dots>
<ul v-show="isHMacBased(otpauthParams.otp_type)"> <p v-show="isHMacBased(otpauthParams.otp_type)">
<li>counter: {{ otpauthParams.counter }}</li> {{ $t('twofaccounts.forms.counter.label') }}: {{ otpauthParams.counter }}
</ul> </p>
<p v-if="user.preferences.showOtpAsDot && user.preferences.revealDottedOTP" class="mt-3">
<button class="button is-ghost has-text-grey-dark" @click.stop="revealPassword = !revealPassword">
<font-awesome-icon v-if="revealPassword" :icon="['fas', 'eye']" />
<font-awesome-icon v-else :icon="['fas', 'eye-slash']" />
</button>
</p>
<TotpLooper <TotpLooper
v-if="hasTOTP" v-if="hasTOTP"
:period="otpauthParams.period" :period="otpauthParams.period"

View File

@ -34,7 +34,7 @@ export function useIdGenerator(fieldType, fieldName) {
} }
} }
export function useDisplayablePassword(pwd) { export function useDisplayablePassword(pwd, reveal = false) {
const user = useUserStore() const user = useUserStore()
if (user.preferences.formatPassword && pwd.length > 0) { if (user.preferences.formatPassword && pwd.length > 0) {
@ -48,5 +48,5 @@ export function useDisplayablePassword(pwd) {
} }
} }
return user.preferences.showOtpAsDot ? pwd.replace(/[0-9]/g, '●') : pwd return user.preferences.showOtpAsDot && !reveal ? pwd.replace(/[0-9]/g, '●') : pwd
} }

View File

@ -165,6 +165,8 @@
<FormCheckbox v-model="user.preferences.copyOtpOnDisplay" @update:model-value="val => savePreference('copyOtpOnDisplay', val)" fieldName="copyOtpOnDisplay" label="settings.forms.copy_otp_on_display.label" help="settings.forms.copy_otp_on_display.help" :isDisabled="!user.preferences.getOtpOnRequest" :isIndented="true" /> <FormCheckbox v-model="user.preferences.copyOtpOnDisplay" @update:model-value="val => savePreference('copyOtpOnDisplay', val)" fieldName="copyOtpOnDisplay" label="settings.forms.copy_otp_on_display.label" help="settings.forms.copy_otp_on_display.help" :isDisabled="!user.preferences.getOtpOnRequest" :isIndented="true" />
<!-- otp as dot --> <!-- otp as dot -->
<FormCheckbox v-model="user.preferences.showOtpAsDot" @update:model-value="val => savePreference('showOtpAsDot', val)" fieldName="showOtpAsDot" label="settings.forms.show_otp_as_dot.label" help="settings.forms.show_otp_as_dot.help" /> <FormCheckbox v-model="user.preferences.showOtpAsDot" @update:model-value="val => savePreference('showOtpAsDot', val)" fieldName="showOtpAsDot" label="settings.forms.show_otp_as_dot.label" help="settings.forms.show_otp_as_dot.help" />
<!-- reveal dotted OTPs -->
<FormCheckbox v-model="user.preferences.revealDottedOTP" @update:model-value="val => savePreference('revealDottedOTP', val)" fieldName="revealDottedOTP" label="settings.forms.reveal_dotted_otp.label" help="settings.forms.reveal_dotted_otp.help" :isDisabled="!user.preferences.showOtpAsDot" :isIndented="true" />
<h4 class="title is-4 pt-4 has-text-grey-light">{{ $t('settings.data_input') }}</h4> <h4 class="title is-4 pt-4 has-text-grey-light">{{ $t('settings.data_input') }}</h4>
<!-- basic qrcode --> <!-- basic qrcode -->
<FormCheckbox v-model="user.preferences.useBasicQrcodeReader" @update:model-value="val => savePreference('useBasicQrcodeReader', val)" fieldName="useBasicQrcodeReader" label="settings.forms.use_basic_qrcode_reader.label" help="settings.forms.use_basic_qrcode_reader.help" /> <FormCheckbox v-model="user.preferences.useBasicQrcodeReader" @update:model-value="val => savePreference('useBasicQrcodeReader', val)" fieldName="useBasicQrcodeReader" label="settings.forms.use_basic_qrcode_reader.label" help="settings.forms.use_basic_qrcode_reader.help" />

View File

@ -34,6 +34,7 @@
const isDragging = ref(false) const isDragging = ref(false)
const isRenewingOTPs = ref(false) const isRenewingOTPs = ref(false)
const renewedPeriod = ref(null) const renewedPeriod = ref(null)
const revealPassword = ref(null)
const otpDisplay = ref(null) const otpDisplay = ref(null)
const otpDisplayProps = ref({ const otpDisplayProps = ref({
@ -398,7 +399,7 @@
<FontAwesomeIcon :icon="['fas', 'circle-notch']" spin /> <FontAwesomeIcon :icon="['fas', 'circle-notch']" spin />
</span> </span>
<span v-else class="always-on-otp is-clickable has-nowrap has-text-grey is-size-5 ml-4" @click="copyToClipboard(account.otp.password)" @keyup.enter="copyToClipboard(account.otp.password)" :title="$t('commons.copy_to_clipboard')"> <span v-else class="always-on-otp is-clickable has-nowrap has-text-grey is-size-5 ml-4" @click="copyToClipboard(account.otp.password)" @keyup.enter="copyToClipboard(account.otp.password)" :title="$t('commons.copy_to_clipboard')">
{{ useDisplayablePassword(account.otp.password) }} {{ useDisplayablePassword(account.otp.password, user.preferences.showOtpAsDot && user.preferences.revealDottedOTP && revealPassword == account.id) }}
</span> </span>
<Dots <Dots
v-if="account.otp_type.includes('totp')" v-if="account.otp_type.includes('totp')"
@ -416,6 +417,16 @@
</span> </span>
</div> </div>
</transition> </transition>
<transition name="popLater" v-if="user.preferences.showOtpAsDot && user.preferences.revealDottedOTP">
<div v-show="user.preferences.getOtpOnRequest == false && !bus.inManagementMode" class="has-text-right">
<button v-if="revealPassword == account.id" class="pr-0 button is-ghost has-text-grey-dark" @click.stop="revealPassword = null">
<font-awesome-icon :icon="['fas', 'eye']" />
</button>
<button v-else class="pr-0 button is-ghost has-text-grey-dark" @click.stop="revealPassword = account.id">
<font-awesome-icon :icon="['fas', 'eye-slash']" />
</button>
</div>
</transition>
<transition name="fadeInOut"> <transition name="fadeInOut">
<div class="tfa-cell tfa-edit has-text-grey" v-if="bus.inManagementMode"> <div class="tfa-cell tfa-edit has-text-grey" v-if="bus.inManagementMode">
<UseColorMode v-slot="{ mode }"> <UseColorMode v-slot="{ mode }">

View File

@ -58,6 +58,10 @@
'label' => 'Show generated <abbr title="One-Time Password">OTP</abbr> as dot', 'label' => 'Show generated <abbr title="One-Time Password">OTP</abbr> as dot',
'help' => 'Replace generated password caracters with *** to ensure confidentiality. Do not affect the copy/paste feature' 'help' => 'Replace generated password caracters with *** to ensure confidentiality. Do not affect the copy/paste feature'
], ],
'reveal_dotted_otp' => [
'label' => 'Reveal obscured <abbr title="One-Time Password">OTP</abbr>',
'help' => 'Let the ability to temporarily reveal Dot-Obscured passwords'
],
'close_otp_on_copy' => [ 'close_otp_on_copy' => [
'label' => 'Close <abbr title="One-Time Password">OTP</abbr> after copy', 'label' => 'Close <abbr title="One-Time Password">OTP</abbr> after copy',
'help' => 'Clicking a generated password to copy it automatically hide it from the screen' 'help' => 'Clicking a generated password to copy it automatically hide it from the screen'

View File

@ -31,6 +31,7 @@
'accounts_deleted' => 'Account(s) successfully deleted', 'accounts_deleted' => 'Account(s) successfully deleted',
'accounts_moved' => 'Account(s) successfully moved', 'accounts_moved' => 'Account(s) successfully moved',
'export_selected_to_json' => 'Download a json export of selected accounts', 'export_selected_to_json' => 'Download a json export of selected accounts',
'reveal' => 'reveal',
'forms' => [ 'forms' => [
'service' => [ 'service' => [
'placeholder' => 'Google, Twitter, Apple', 'placeholder' => 'Google, Twitter, Apple',