Browse Source

fix otls

pull/1719/head
Bernd Storath 5 months ago
parent
commit
251f2a1c00
  1. 61
      src/app/components/ClientCard/OneTimeLink.vue
  2. 7
      src/server/database/repositories/client/schema.ts
  3. 4
      src/server/database/repositories/client/service.ts
  4. 25
      src/server/database/repositories/oneTimeLink/service.ts
  5. 24
      src/server/routes/cnf/[oneTimeLink].ts
  6. 16
      src/server/utils/WireGuard.ts

61
src/app/components/ClientCard/OneTimeLink.vue

@ -1,45 +1,50 @@
<template>
<div v-if="client.oneTimeLink !== null" class="text-xs text-gray-400">
<a :href="'./cnf/' + client.oneTimeLink.oneTimeLink">{{ path }}</a>
<div v-if="modifiedOtls.length > 0" class="text-xs text-gray-400">
<div v-for="link in modifiedOtls" :key="link.oneTimeLink">
<a :href="'./cnf/' + link.oneTimeLink">{{ link.path ?? 'Loading' }}</a>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{ client: LocalClient }>();
const path = ref('Loading...');
const modifiedOtls = ref<
(LocalClient['oneTimeLinks'][number] & { path?: string })[]
>(props.client.oneTimeLinks);
const timer = ref<NodeJS.Timeout | null>(null);
const { localeProperties } = useI18n();
onMounted(() => {
timer.value = setIntervalImmediately(() => {
if (props.client.oneTimeLink === null) {
return;
}
const timeLeft =
new Date(props.client.oneTimeLink.expiresAt).getTime() - Date.now();
if (timeLeft <= 0) {
path.value = `${document.location.protocol}//${document.location.host}/cnf/${props.client.oneTimeLink.oneTimeLink} (00:00)`;
return;
for (const link of modifiedOtls.value) {
const timeLeft = new Date(link.expiresAt).getTime() - Date.now();
if (timeLeft <= 0) {
link.path = `${document.location.protocol}//${document.location.host}/cnf/${link.oneTimeLink} (00:00)`;
continue;
}
const formatter = new Intl.DateTimeFormat(
localeProperties.value.language,
{
minute: '2-digit',
second: '2-digit',
hourCycle: 'h23',
}
);
const minutes = Math.floor(timeLeft / 60000);
const seconds = Math.floor((timeLeft % 60000) / 1000);
const date = new Date(0);
date.setMinutes(minutes);
date.setSeconds(seconds);
link.path = `${document.location.protocol}//${document.location.host}/cnf/${link.oneTimeLink} (${formatter.format(date)})`;
}
const formatter = new Intl.DateTimeFormat(localeProperties.value.language, {
minute: '2-digit',
second: '2-digit',
hourCycle: 'h23',
});
const minutes = Math.floor(timeLeft / 60000);
const seconds = Math.floor((timeLeft % 60000) / 1000);
const date = new Date(0);
date.setMinutes(minutes);
date.setSeconds(seconds);
path.value = `${document.location.protocol}//${document.location.host}/cnf/${props.client.oneTimeLink.oneTimeLink} (${formatter.format(date)})`;
}, 1000);
});

7
src/server/database/repositories/client/schema.ts

@ -39,11 +39,8 @@ export const client = sqliteTable('clients_table', {
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`),
});
export const clientsRelations = relations(client, ({ one }) => ({
oneTimeLink: one(oneTimeLink, {
fields: [client.id],
references: [oneTimeLink.clientId],
}),
export const clientsRelations = relations(client, ({ one, many }) => ({
oneTimeLinks: many(oneTimeLink),
user: one(user, {
fields: [client.userId],
references: [user.id],

4
src/server/database/repositories/client/service.ts

@ -14,7 +14,7 @@ function createPreparedStatement(db: DBType) {
findAll: db.query.client
.findMany({
with: {
oneTimeLink: true,
oneTimeLinks: true,
},
})
.prepare(),
@ -24,7 +24,7 @@ function createPreparedStatement(db: DBType) {
findByUserId: db.query.client
.findMany({
where: eq(client.userId, sql.placeholder('userId')),
with: { oneTimeLink: true },
with: { oneTimeLinks: true },
})
.prepare(),
toggle: db

25
src/server/database/repositories/oneTimeLink/service.ts

@ -12,7 +12,7 @@ function createPreparedStatement(db: DBType) {
create: db
.insert(oneTimeLink)
.values({
clientId: sql.placeholder('id'),
clientId: sql.placeholder('clientId'),
oneTimeLink: sql.placeholder('oneTimeLink'),
expiresAt: sql.placeholder('expiresAt'),
})
@ -20,7 +20,12 @@ function createPreparedStatement(db: DBType) {
erase: db
.update(oneTimeLink)
.set({ expiresAt: sql.placeholder('expiresAt') as never as string })
.where(eq(oneTimeLink.clientId, sql.placeholder('id')))
.where(eq(oneTimeLink.id, sql.placeholder('id')))
.prepare(),
findByOneTimeLink: db.query.oneTimeLink
.findFirst({
where: eq(oneTimeLink.oneTimeLink, sql.placeholder('oneTimeLink')),
})
.prepare(),
};
}
@ -36,16 +41,24 @@ export class OneTimeLinkService {
return this.#statements.delete.execute({ id });
}
generate(id: ID) {
const key = `${id}-${Math.floor(Math.random() * 1000)}`;
getByOtl(oneTimeLink: string) {
return this.#statements.findByOneTimeLink.execute({ oneTimeLink });
}
generate(clientId: ID) {
const key = `${clientId}-${Math.floor(Math.random() * 1000)}`;
const oneTimeLink = Math.abs(CRC32.str(key)).toString(16);
const expiresAt = new Date(Date.now() + 5 * 60 * 1000).toISOString();
return this.#statements.create.execute({ id, oneTimeLink, expiresAt });
return this.#statements.create.execute({
clientId,
oneTimeLink,
expiresAt,
});
}
erase(id: ID) {
const expiresAt = Date.now() + 10 * 1000;
const expiresAt = new Date(Date.now() + 10 * 1000).toISOString();
return this.#statements.erase.execute({ id, expiresAt });
}
}

24
src/server/routes/cnf/[oneTimeLink].ts

@ -5,20 +5,28 @@ export default defineEventHandler(async (event) => {
event,
validateZod(OneTimeLinkGetSchema, event)
);
const clients = await WireGuard.getAllClients();
// TODO: filter on the database level
const client = clients.find(
(client) => client.oneTimeLink?.oneTimeLink === oneTimeLink
);
const otl = await Database.oneTimeLinks.getByOtl(oneTimeLink);
if (!otl) {
throw createError({
statusCode: 404,
statusMessage: 'Invalid One Time Link',
});
}
const client = await Database.clients.get(otl.clientId);
if (!client) {
throw createError({
statusCode: 404,
statusMessage: 'Invalid One Time Link',
});
}
const clientId = client.id;
const config = await WireGuard.getClientConfiguration({ clientId });
await Database.oneTimeLinks.erase(clientId);
const config = await WireGuard.getClientConfiguration({
clientId: client.id,
});
await Database.oneTimeLinks.erase(otl.id);
setHeader(
event,
'Content-Disposition',

16
src/server/utils/WireGuard.ts

@ -208,12 +208,13 @@ class WireGuard {
}
// One Time Link Feature
for (const client of clients) {
if (
client.oneTimeLink !== null &&
new Date() > new Date(client.oneTimeLink.expiresAt)
) {
WG_DEBUG(`Client ${client.id} One Time Link expired.`);
await Database.oneTimeLinks.delete(client.oneTimeLink.id);
for (const oneTimeLink of client.oneTimeLinks) {
if (new Date() > new Date(oneTimeLink.expiresAt)) {
WG_DEBUG(
`OneTimeLink ${oneTimeLink.id} for Client ${client.id} expired.`
);
await Database.oneTimeLinks.delete(oneTimeLink.id);
}
}
}
@ -222,11 +223,10 @@ class WireGuard {
}
if (OLD_ENV.PASSWORD || OLD_ENV.PASSWORD_HASH) {
// TODO: change url before release
throw new Error(
`
You are using an invalid Configuration for wg-easy
Please follow the instructions on https://wg-easy.github.io/wg-easy/ to migrate
Please follow the instructions on https://wg-easy.github.io/wg-easy/latest/advanced/migrate/from-14-to-15/ to migrate
`
);
}

Loading…
Cancel
Save