chore(lint): switched to a better lint config

This commit is contained in:
Corentin Thomasset 2023-05-28 23:13:24 +02:00 committed by Corentin THOMASSET
parent 4d2b037dbe
commit 33c9b6643f
178 changed files with 4105 additions and 3371 deletions

View file

@ -1,86 +1,13 @@
<template>
<div style="max-width: 350px">
<c-input-text
v-model:value="secret"
label="Secret"
placeholder="Paste your TOTP secret..."
mb-5
:validation-rules="secretValidationRules"
>
<template #suffix>
<n-tooltip trigger="hover">
<template #trigger>
<c-button circle variant="text" size="small" @click="refreshSecret">
<icon-mdi-refresh />
</c-button>
</template>
Generate secret token
</n-tooltip>
</template>
</c-input-text>
<div>
<token-display :tokens="tokens" style="margin-top: 2px" />
<n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" />
<div style="text-align: center">Next in {{ String(Math.floor(30 - interval)).padStart(2, '0') }}s</div>
</div>
<div mt-4 flex flex-col items-center justify-center gap-3>
<n-image :src="qrcode"></n-image>
<c-button :href="keyUri" target="_blank">Open Key URI in new tab</c-button>
</div>
</div>
<div style="max-width: 350px">
<input-copyable
label="Secret in hexadecimal"
:value="base32toHex(secret)"
readonly
placeholder="Secret in hex will be displayed here"
mb-5
/>
<input-copyable
label="Epoch"
:value="Math.floor(now / 1000).toString()"
readonly
mb-5
placeholder="Epoch in sec will be displayed here"
/>
<p>Iteration</p>
<input-copyable
:value="String(getCounterFromTime({ now, timeStep: 30 }))"
readonly
label="Count:"
label-position="left"
label-width="90px"
label-align="right"
placeholder="Iteration count will be displayed here"
/>
<input-copyable
:value="getCounterFromTime({ now, timeStep: 30 }).toString(16).padStart(16, '0')"
readonly
placeholder="Iteration count in hex will be displayed here"
label-position="left"
label-width="90px"
label-align="right"
label="Padded hex:"
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useTimestamp } from '@vueuse/core';
import { useThemeVars } from 'naive-ui';
import { useQRCode } from '../qr-code-generator/useQRCode';
import { base32toHex, buildKeyUri, generateSecret, generateTOTP, getCounterFromTime } from './otp.service';
import TokenDisplay from './token-display.vue';
import { useStyleStore } from '@/stores/style.store';
import InputCopyable from '@/components/InputCopyable.vue';
import { computedRefreshable } from '@/composable/computedRefreshable';
import { generateTOTP, buildKeyUri, generateSecret, base32toHex, getCounterFromTime } from './otp.service';
import { useQRCode } from '../qr-code-generator/useQRCode';
import TokenDisplay from './token-display.vue';
const now = useTimestamp();
const interval = computed(() => (now.value / 1000) % 30);
@ -125,6 +52,83 @@ const secretValidationRules = [
];
</script>
<template>
<div style="max-width: 350px">
<c-input-text
v-model:value="secret"
label="Secret"
placeholder="Paste your TOTP secret..."
mb-5
:validation-rules="secretValidationRules"
>
<template #suffix>
<n-tooltip trigger="hover">
<template #trigger>
<c-button circle variant="text" size="small" @click="refreshSecret">
<icon-mdi-refresh />
</c-button>
</template>
Generate secret token
</n-tooltip>
</template>
</c-input-text>
<div>
<TokenDisplay :tokens="tokens" style="margin-top: 2px" />
<n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" />
<div style="text-align: center">
Next in {{ String(Math.floor(30 - interval)).padStart(2, '0') }}s
</div>
</div>
<div mt-4 flex flex-col items-center justify-center gap-3>
<n-image :src="qrcode" />
<c-button :href="keyUri" target="_blank">
Open Key URI in new tab
</c-button>
</div>
</div>
<div style="max-width: 350px">
<InputCopyable
label="Secret in hexadecimal"
:value="base32toHex(secret)"
readonly
placeholder="Secret in hex will be displayed here"
mb-5
/>
<InputCopyable
label="Epoch"
:value="Math.floor(now / 1000).toString()"
readonly
mb-5
placeholder="Epoch in sec will be displayed here"
/>
<p>Iteration</p>
<InputCopyable
:value="String(getCounterFromTime({ now, timeStep: 30 }))"
readonly
label="Count:"
label-position="left"
label-width="90px"
label-align="right"
placeholder="Iteration count will be displayed here"
/>
<InputCopyable
:value="getCounterFromTime({ now, timeStep: 30 }).toString(16).padStart(16, '0')"
readonly
placeholder="Iteration count in hex will be displayed here"
label-position="left"
label-width="90px"
label-align="right"
label="Padded hex:"
/>
</div>
</template>
<style lang="less" scoped>
.n-progress {
margin-top: 10px;

View file

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { expect, test } from '@playwright/test';
test.describe('Tool - OTP code generator', () => {
test.beforeEach(async ({ page }) => {
@ -19,7 +19,7 @@ test.describe('Tool - OTP code generator', () => {
test('OTP a generated from the provided secret', async ({ page }) => {
page.evaluate(() => {
Date.now = () => 1609477200000; //Jan 1, 2021
Date.now = () => 1609477200000; // Jan 1, 2021
});
await page.getByPlaceholder('Paste your TOTP secret...').fill('ITTOOLS');

View file

@ -1,12 +1,12 @@
import { describe, expect, it } from 'vitest';
import {
base32toHex,
buildKeyUri,
generateHOTP,
generateTOTP,
hexToBytes,
verifyHOTP,
generateTOTP,
verifyTOTP,
buildKeyUri,
base32toHex,
} from './otp.service';
describe('otp functions', () => {

View file

@ -1,4 +1,4 @@
import { enc, HmacSHA1 } from 'crypto-js';
import { HmacSHA1, enc } from 'crypto-js';
import _ from 'lodash';
import { createToken } from '../token-generator/token-generator.service';
@ -15,7 +15,7 @@ export {
};
function hexToBytes(hex: string) {
return (hex.match(/.{1,2}/g) ?? []).map((char) => parseInt(char, 16));
return (hex.match(/.{1,2}/g) ?? []).map(char => parseInt(char, 16));
}
function computeHMACSha1(message: string, key: string) {
@ -29,10 +29,10 @@ function base32toHex(base32: string) {
.toUpperCase() // Since base 32, we coerce lowercase to uppercase
.replace(/=+$/, '')
.split('')
.map((value) => base32Chars.indexOf(value).toString(2).padStart(5, '0'))
.map(value => base32Chars.indexOf(value).toString(2).padStart(5, '0'))
.join('');
const hex = (bits.match(/.{1,8}/g) ?? []).map((chunk) => parseInt(chunk, 2).toString(16).padStart(2, '0')).join('');
const hex = (bits.match(/.{1,8}/g) ?? []).map(chunk => parseInt(chunk, 2).toString(16).padStart(2, '0')).join('');
return hex;
}
@ -45,12 +45,12 @@ function generateHOTP({ key, counter = 0 }: { key: string; counter?: number }) {
const bytes = hexToBytes(digest);
// Truncate
const offset = bytes[19] & 0xf;
const v =
((bytes[offset] & 0x7f) << 24) |
((bytes[offset + 1] & 0xff) << 16) |
((bytes[offset + 2] & 0xff) << 8) |
(bytes[offset + 3] & 0xff);
const offset = bytes[19] & 0xF;
const v
= ((bytes[offset] & 0x7F) << 24)
| ((bytes[offset + 1] & 0xFF) << 16)
| ((bytes[offset + 2] & 0xFF) << 8)
| (bytes[offset + 3] & 0xFF);
const code = String(v % 1000000).padStart(6, '0');
@ -63,10 +63,10 @@ function verifyHOTP({
window = 0,
counter = 0,
}: {
token: string;
key: string;
window?: number;
counter?: number;
token: string
key: string
window?: number
counter?: number
}) {
for (let i = counter - window; i <= counter + window; ++i) {
if (generateHOTP({ key, counter: i }) === token) {
@ -94,11 +94,11 @@ function verifyTOTP({
now = Date.now(),
timeStep = 30,
}: {
token: string;
key: string;
window?: number;
now?: number;
timeStep?: number;
token: string
key: string
window?: number
now?: number
timeStep?: number
}) {
const counter = getCounterFromTime({ now, timeStep });
@ -113,12 +113,12 @@ function buildKeyUri({
digits = 6,
period = 30,
}: {
secret: string;
app?: string;
account?: string;
algorithm?: string;
digits?: number;
period?: number;
secret: string
app?: string
account?: string
algorithm?: string
digits?: number
period?: number
}) {
const params = {
issuer: app,

View file

@ -1,9 +1,27 @@
<script setup lang="ts">
import { useClipboard } from '@vueuse/core';
import { toRefs } from 'vue';
const props = defineProps<{ tokens: { previous: string; current: string; next: string } }>();
const { copy: copyPrevious, copied: previousCopied } = useClipboard();
const { copy: copyCurrent, copied: currentCopied } = useClipboard();
const { copy: copyNext, copied: nextCopied } = useClipboard();
const { tokens } = toRefs(props);
</script>
<template>
<div>
<div class="labels" w-full flex items-center>
<div flex-1 text-left>Previous</div>
<div flex-1 text-center>Current OTP</div>
<div flex-1 text-right>Next</div>
<div flex-1 text-left>
Previous
</div>
<div flex-1 text-center>
Current OTP
</div>
<div flex-1 text-right>
Next
</div>
</div>
<n-input-group>
<n-tooltip trigger="hover" placement="bottom">
@ -29,9 +47,11 @@
</n-tooltip>
<n-tooltip trigger="hover" placement="bottom">
<template #trigger>
<c-button important:h-12 data-test-id="next-otp" @click.prevent="copyNext(tokens.next)">{{
tokens.next
}}</c-button>
<c-button important:h-12 data-test-id="next-otp" @click.prevent="copyNext(tokens.next)">
{{
tokens.next
}}
</c-button>
</template>
<div>{{ nextCopied ? 'Copied !' : 'Copy next OTP' }}</div>
</n-tooltip>
@ -39,18 +59,6 @@
</div>
</template>
<script setup lang="ts">
import { useClipboard } from '@vueuse/core';
import { toRefs } from 'vue';
const { copy: copyPrevious, copied: previousCopied } = useClipboard();
const { copy: copyCurrent, copied: currentCopied } = useClipboard();
const { copy: copyNext, copied: nextCopied } = useClipboard();
const props = defineProps<{ tokens: { previous: string; current: string; next: string } }>();
const { tokens } = toRefs(props);
</script>
<style scoped lang="less">
.current-otp {
font-size: 22px;