mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
/api/client/[id]/key, /api/mek/share Endpoint 추가
This commit is contained in:
@@ -62,6 +62,16 @@ export const getUserClient = async (userId: number, clientId: number) => {
|
|||||||
return userClients[0] ?? null;
|
return userClients[0] ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getUserClientWithDetails = async (userId: number, clientId: number) => {
|
||||||
|
const userClients = await db
|
||||||
|
.select()
|
||||||
|
.from(userClient)
|
||||||
|
.innerJoin(client, eq(userClient.clientId, client.id))
|
||||||
|
.where(and(eq(userClient.userId, userId), eq(userClient.clientId, clientId)))
|
||||||
|
.execute();
|
||||||
|
return userClients[0] ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
export const setUserClientStateToPending = async (userId: number, clientId: number) => {
|
export const setUserClientStateToPending = async (userId: number, clientId: number) => {
|
||||||
await db
|
await db
|
||||||
.update(userClient)
|
.update(userClient)
|
||||||
|
|||||||
@@ -2,32 +2,21 @@ import { and, or, eq, lt, desc } from "drizzle-orm";
|
|||||||
import db from "./drizzle";
|
import db from "./drizzle";
|
||||||
import { mek, clientMek, userClient } from "./schema";
|
import { mek, clientMek, userClient } from "./schema";
|
||||||
|
|
||||||
interface ClientMek {
|
|
||||||
clientId: number;
|
|
||||||
encMek: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const registerInitialMek = async (userId: number, createdBy: number, encMek: string) => {
|
export const registerInitialMek = async (userId: number, createdBy: number, encMek: string) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
await tx
|
await tx.insert(mek).values({
|
||||||
.insert(mek)
|
userId,
|
||||||
.values({
|
version: 1,
|
||||||
userId,
|
createdBy,
|
||||||
version: 1,
|
createdAt: new Date(),
|
||||||
createdBy,
|
state: "active",
|
||||||
createdAt: new Date(),
|
});
|
||||||
state: "active",
|
await tx.insert(clientMek).values({
|
||||||
})
|
userId,
|
||||||
.execute();
|
clientId: createdBy,
|
||||||
await tx
|
mekVersion: 1,
|
||||||
.insert(clientMek)
|
encMek,
|
||||||
.values({
|
});
|
||||||
userId,
|
|
||||||
clientId: createdBy,
|
|
||||||
mekVersion: 1,
|
|
||||||
encMek,
|
|
||||||
})
|
|
||||||
.execute();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,7 +24,10 @@ export const registerActiveMek = async (
|
|||||||
userId: number,
|
userId: number,
|
||||||
version: number,
|
version: number,
|
||||||
createdBy: number,
|
createdBy: number,
|
||||||
clientMeks: ClientMek[],
|
clientMeks: {
|
||||||
|
clientId: number;
|
||||||
|
encMek: string;
|
||||||
|
}[],
|
||||||
) => {
|
) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
// 1. Check if the clientMeks are valid
|
// 1. Check if the clientMeks are valid
|
||||||
@@ -59,34 +51,35 @@ export const registerActiveMek = async (
|
|||||||
state: "retired",
|
state: "retired",
|
||||||
retiredAt: new Date(),
|
retiredAt: new Date(),
|
||||||
})
|
})
|
||||||
.where(and(eq(mek.userId, userId), lt(mek.version, version), eq(mek.state, "active")))
|
.where(and(eq(mek.userId, userId), lt(mek.version, version), eq(mek.state, "active")));
|
||||||
.execute();
|
await tx.insert(mek).values({
|
||||||
await tx
|
userId,
|
||||||
.insert(mek)
|
version,
|
||||||
.values({
|
createdBy,
|
||||||
userId,
|
createdAt: new Date(),
|
||||||
version,
|
state: "active",
|
||||||
createdBy,
|
});
|
||||||
createdAt: new Date(),
|
|
||||||
state: "active",
|
|
||||||
})
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
// 3. Insert the new client MEKs
|
// 3. Insert the new client MEKs
|
||||||
await tx
|
await tx.insert(clientMek).values(
|
||||||
.insert(clientMek)
|
clientMeks.map(({ clientId, encMek }) => ({
|
||||||
.values(
|
userId,
|
||||||
clientMeks.map(({ clientId, encMek }) => ({
|
clientId,
|
||||||
userId,
|
mekVersion: version,
|
||||||
clientId,
|
encMek,
|
||||||
mekVersion: version,
|
})),
|
||||||
encMek,
|
);
|
||||||
})),
|
|
||||||
)
|
|
||||||
.execute();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAllValidMeks = async (userId: number) => {
|
||||||
|
return await db
|
||||||
|
.select()
|
||||||
|
.from(mek)
|
||||||
|
.where(and(eq(mek.userId, userId), or(eq(mek.state, "active"), eq(mek.state, "retired"))))
|
||||||
|
.execute();
|
||||||
|
};
|
||||||
|
|
||||||
export const getInitialMek = async (userId: number) => {
|
export const getInitialMek = async (userId: number) => {
|
||||||
const meks = await db
|
const meks = await db
|
||||||
.select()
|
.select()
|
||||||
@@ -110,6 +103,54 @@ export const getNextActiveMekVersion = async (userId: number) => {
|
|||||||
return meks[0].version + 1;
|
return meks[0].version + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const registerClientMeks = async (
|
||||||
|
userId: number,
|
||||||
|
clientId: number,
|
||||||
|
clientMeks: {
|
||||||
|
version: number;
|
||||||
|
encMek: string;
|
||||||
|
}[],
|
||||||
|
) => {
|
||||||
|
await db.transaction(async (tx) => {
|
||||||
|
// 1. Check if the client is valid
|
||||||
|
const userClients = await tx
|
||||||
|
.select()
|
||||||
|
.from(userClient)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userClient.userId, userId),
|
||||||
|
eq(userClient.clientId, clientId),
|
||||||
|
eq(userClient.state, "active"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (userClients.length === 0) {
|
||||||
|
throw new Error("Invalid client");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check if the clientMeks are valid
|
||||||
|
const meks = await tx
|
||||||
|
.select()
|
||||||
|
.from(mek)
|
||||||
|
.where(and(eq(mek.userId, userId), or(eq(mek.state, "active"), eq(mek.state, "retired"))));
|
||||||
|
if (
|
||||||
|
clientMeks.length !== meks.length ||
|
||||||
|
!clientMeks.every((clientMek) => meks.some((mek) => mek.version === clientMek.version))
|
||||||
|
) {
|
||||||
|
throw new Error("Invalid key list");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Insert the client MEKs
|
||||||
|
await tx.insert(clientMek).values(
|
||||||
|
clientMeks.map(({ version, encMek }) => ({
|
||||||
|
userId,
|
||||||
|
clientId,
|
||||||
|
mekVersion: version,
|
||||||
|
encMek,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getAllValidClientMeks = async (userId: number, clientId: number) => {
|
export const getAllValidClientMeks = async (userId: number, clientId: number) => {
|
||||||
return await db
|
return await db
|
||||||
.select()
|
.select()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { user } from "./user";
|
|||||||
export const client = sqliteTable(
|
export const client = sqliteTable(
|
||||||
"client",
|
"client",
|
||||||
{
|
{
|
||||||
id: integer("id").primaryKey(),
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
encPubKey: text("encryption_public_key").notNull().unique(), // Base64
|
encPubKey: text("encryption_public_key").notNull().unique(), // Base64
|
||||||
sigPubKey: text("signature_public_key").notNull().unique(), // Base64
|
sigPubKey: text("signature_public_key").notNull().unique(), // Base64
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
export const user = sqliteTable("user", {
|
export const user = sqliteTable("user", {
|
||||||
id: integer("id").primaryKey(),
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
email: text("email").notNull().unique(),
|
email: text("email").notNull().unique(),
|
||||||
password: text("password").notNull(),
|
password: text("password").notNull(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
createUserClient,
|
createUserClient,
|
||||||
getAllUserClients,
|
getAllUserClients,
|
||||||
getUserClient,
|
getUserClient,
|
||||||
|
getUserClientWithDetails,
|
||||||
setUserClientStateToPending,
|
setUserClientStateToPending,
|
||||||
registerUserClientChallenge,
|
registerUserClientChallenge,
|
||||||
getUserClientChallenge,
|
getUserClientChallenge,
|
||||||
@@ -17,6 +18,15 @@ import { verifyPubKey, verifySignature, generateChallenge } from "$lib/server/mo
|
|||||||
import { isInitialMekNeeded } from "$lib/server/modules/mek";
|
import { isInitialMekNeeded } from "$lib/server/modules/mek";
|
||||||
import env from "$lib/server/loadenv";
|
import env from "$lib/server/loadenv";
|
||||||
|
|
||||||
|
export const getUserClientEncPubKey = async (userId: number, clientId: number) => {
|
||||||
|
const userClient = await getUserClientWithDetails(userId, clientId);
|
||||||
|
if (!userClient || userClient.user_client.state === "challenging") {
|
||||||
|
error(400, "Invalid client ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { encPubKey: userClient.client.encPubKey };
|
||||||
|
};
|
||||||
|
|
||||||
export const getUserClientList = async (userId: number) => {
|
export const getUserClientList = async (userId: number) => {
|
||||||
const userClients = await getAllUserClients(userId);
|
const userClients = await getAllUserClients(userId);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
import { getAllUserClients, setUserClientStateToActive } from "$lib/server/db/client";
|
import { getAllUserClients, setUserClientStateToActive } from "$lib/server/db/client";
|
||||||
import {
|
import {
|
||||||
getAllValidClientMeks,
|
|
||||||
registerInitialMek,
|
registerInitialMek,
|
||||||
registerActiveMek,
|
registerActiveMek,
|
||||||
|
getAllValidMeks,
|
||||||
getNextActiveMekVersion,
|
getNextActiveMekVersion,
|
||||||
|
registerClientMeks,
|
||||||
|
getAllValidClientMeks,
|
||||||
} from "$lib/server/db/mek";
|
} from "$lib/server/db/mek";
|
||||||
import { isInitialMekNeeded } from "$lib/server/modules/mek";
|
import { isInitialMekNeeded } from "$lib/server/modules/mek";
|
||||||
|
|
||||||
interface NewClientMek {
|
|
||||||
clientId: number;
|
|
||||||
encMek: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getClientMekList = async (userId: number, clientId: number) => {
|
export const getClientMekList = async (userId: number, clientId: number) => {
|
||||||
const clientMeks = await getAllValidClientMeks(userId, clientId);
|
const clientMeks = await getAllValidClientMeks(userId, clientId);
|
||||||
return {
|
return {
|
||||||
@@ -40,7 +37,10 @@ export const registerInitialActiveMek = async (
|
|||||||
export const registerNewActiveMek = async (
|
export const registerNewActiveMek = async (
|
||||||
userId: number,
|
userId: number,
|
||||||
createdBy: number,
|
createdBy: number,
|
||||||
clientMeks: NewClientMek[],
|
clientMeks: {
|
||||||
|
clientId: number;
|
||||||
|
encMek: string;
|
||||||
|
}[],
|
||||||
) => {
|
) => {
|
||||||
const userClients = await getAllUserClients(userId);
|
const userClients = await getAllUserClients(userId);
|
||||||
const activeUserClients = userClients.filter(({ state }) => state === "active");
|
const activeUserClients = userClients.filter(({ state }) => state === "active");
|
||||||
@@ -56,3 +56,22 @@ export const registerNewActiveMek = async (
|
|||||||
const newMekVersion = await getNextActiveMekVersion(userId);
|
const newMekVersion = await getNextActiveMekVersion(userId);
|
||||||
await registerActiveMek(userId, newMekVersion, createdBy, clientMeks);
|
await registerActiveMek(userId, newMekVersion, createdBy, clientMeks);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const shareMeksForNewClient = async (
|
||||||
|
userId: number,
|
||||||
|
targetClientId: number,
|
||||||
|
clientMeks: {
|
||||||
|
version: number;
|
||||||
|
encMek: string;
|
||||||
|
}[],
|
||||||
|
) => {
|
||||||
|
const meks = await getAllValidMeks(userId);
|
||||||
|
if (
|
||||||
|
clientMeks.length !== meks.length ||
|
||||||
|
!clientMeks.every((clientMek) => meks.some((mek) => mek.version === clientMek.version))
|
||||||
|
) {
|
||||||
|
error(400, "Invalid key list");
|
||||||
|
}
|
||||||
|
|
||||||
|
await registerClientMeks(userId, targetClientId, clientMeks);
|
||||||
|
};
|
||||||
|
|||||||
20
src/routes/api/client/[id]/key/+server.ts
Normal file
20
src/routes/api/client/[id]/key/+server.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { error, json } from "@sveltejs/kit";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { authorize } from "$lib/server/modules/auth";
|
||||||
|
import { getUserClientEncPubKey } from "$lib/server/services/client";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ cookies, params }) => {
|
||||||
|
const { userId } = await authorize(cookies, "activeClient");
|
||||||
|
|
||||||
|
const zodRes = z
|
||||||
|
.object({
|
||||||
|
id: z.coerce.number().int().positive(),
|
||||||
|
})
|
||||||
|
.safeParse(params);
|
||||||
|
if (!zodRes.success) error(400, "Invalid path parameters");
|
||||||
|
const { id } = zodRes.data;
|
||||||
|
|
||||||
|
const { encPubKey } = await getUserClientEncPubKey(userId, id);
|
||||||
|
return json({ encPubKey });
|
||||||
|
};
|
||||||
30
src/routes/api/mek/share/+server.ts
Normal file
30
src/routes/api/mek/share/+server.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { text } from "@sveltejs/kit";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { authorize } from "$lib/server/modules/auth";
|
||||||
|
import { parseSignedRequest } from "$lib/server/modules/crypto";
|
||||||
|
import { shareMeksForNewClient } from "$lib/server/services/mek";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
|
||||||
|
export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||||
|
const { userId, clientId } = await authorize(cookies, "activeClient");
|
||||||
|
const { targetId, meks } = await parseSignedRequest(
|
||||||
|
clientId,
|
||||||
|
await request.json(),
|
||||||
|
z.object({
|
||||||
|
targetId: z.number().int().positive(),
|
||||||
|
meks: z.array(
|
||||||
|
z.object({
|
||||||
|
version: z.number().int().positive(),
|
||||||
|
mek: z.string().base64().nonempty(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await shareMeksForNewClient(
|
||||||
|
userId,
|
||||||
|
targetId,
|
||||||
|
meks.map(({ version, mek }) => ({ version, encMek: mek })),
|
||||||
|
);
|
||||||
|
return text("MEK shared", { headers: { "Content-Type": "text/plain" } });
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user