mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-11-04 08:10:28 +09:00
리다이렉트 경로 처리, 파일 정보 변환, 상태 표시 UI 등 중복된 코드를 통합하고 모듈 간 순환 의존성 및 계층 위반 해소
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9119c21881
commit
5b8621e524
96 files changed
+452
-548
No files matched your search
+11
-15
@@ -1,7 +1,7 @@
|
||||
import { ClientRepo, SessionRepo, IntegrityError } from "#lib/server/db/index.ts";
|
||||
import { ClientRepo, SessionRepo } from "#lib/server/db/index.ts";
|
||||
import env from "#lib/server/config.ts";
|
||||
import { verifySessionId } from "#lib/server/crypto.ts";
|
||||
import { AppError } from "#lib/server/errors.ts";
|
||||
import { AppError, invalidSessionError } from "#lib/server/errors.ts";
|
||||
|
||||
export interface Session {
|
||||
sessionId: string;
|
||||
@@ -39,19 +39,15 @@ export const authenticate = async (sessionIdSigned: string, ip: string, userAgen
|
||||
throw new AuthenticationError(400, "Invalid session id");
|
||||
}
|
||||
|
||||
try {
|
||||
const { userId, clientId } = await SessionRepo.refreshSession(sessionId, ip, userAgent);
|
||||
return {
|
||||
id: sessionId,
|
||||
userId,
|
||||
clientId: clientId ?? undefined,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof IntegrityError && e.message === "Session not found") {
|
||||
throw new AuthenticationError(401, "Invalid session id");
|
||||
}
|
||||
throw e;
|
||||
const session = await SessionRepo.refreshSession(sessionId, ip, userAgent);
|
||||
if (!session) {
|
||||
throw new AuthenticationError(401, "Invalid session id");
|
||||
}
|
||||
return {
|
||||
id: sessionId,
|
||||
userId: session.userId,
|
||||
clientId: session.clientId ?? undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const ensureUserClientState = async (
|
||||
@@ -65,7 +61,7 @@ const ensureUserClientState = async (
|
||||
|
||||
const userClient = await ClientRepo.getUserClient(userId, clientId);
|
||||
if (!userClient) {
|
||||
throw new AppError("INTERNAL_SERVER_ERROR", "Invalid session id");
|
||||
throw invalidSessionError();
|
||||
} else if (userClient.state !== state) {
|
||||
throw new AppError("FORBIDDEN", "Forbidden");
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const errorCodes = {
|
||||
"Inactive MEK version": "BAD_REQUEST",
|
||||
// Session
|
||||
"Session not found": "UNAUTHORIZED",
|
||||
"Session already exists": "CONFLICT",
|
||||
"Already logged in": "CONFLICT",
|
||||
// Upload
|
||||
"Upload session not found": "NOT_FOUND",
|
||||
} as const satisfies Record<string, ErrorCode>;
|
||||
|
||||
@@ -40,10 +40,7 @@ export const refreshSession = async (
|
||||
.where("last_used_at", ">", new Date(now.getTime() - env.session.exp))
|
||||
.returning(["user_id", "client_id"])
|
||||
.executeTakeFirst();
|
||||
if (!session) {
|
||||
throw new IntegrityError("Session not found");
|
||||
}
|
||||
return { userId: session.user_id, clientId: session.client_id };
|
||||
return session ? { userId: session.user_id, clientId: session.client_id } : null;
|
||||
};
|
||||
|
||||
export const upgradeSession = async (
|
||||
@@ -74,7 +71,7 @@ export const upgradeSession = async (
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof pg.DatabaseError && e.code === "23505") {
|
||||
throw new IntegrityError("Session already exists");
|
||||
throw new IntegrityError("Already logged in");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -31,3 +31,7 @@ export class AppError extends Error {
|
||||
return httpStatuses[this.code];
|
||||
}
|
||||
}
|
||||
|
||||
// The user or the client of an authenticated session doesn't exist, which shouldn't happen
|
||||
export const invalidSessionError = () =>
|
||||
new AppError("INTERNAL_SERVER_ERROR", "Invalid session id");
|
||||
@@ -1,8 +1,8 @@
|
||||
import { error, type RequestEvent } from "@sveltejs/kit";
|
||||
import { error } from "@sveltejs/kit";
|
||||
import { AppError } from "#lib/server/errors.ts";
|
||||
|
||||
// Converts `AppError`s thrown by services into HTTP errors
|
||||
export const handleAppErrors = <Event extends RequestEvent>(
|
||||
export const handleAppErrors = <Event>(
|
||||
handler: (event: Event) => Promise<Response>,
|
||||
): ((event: Event) => Promise<Response>) => {
|
||||
return async (event) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { error, redirect } from "@sveltejs/kit";
|
||||
import type { Handle } from "@sveltejs/kit/hooks";
|
||||
import { cookieOptions, authenticate, AuthenticationError } from "#lib/server/auth.ts";
|
||||
import { getRedirectPath, withRedirectPath } from "#lib/shared/redirect.ts";
|
||||
|
||||
export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
||||
try {
|
||||
@@ -20,16 +21,14 @@ export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
||||
} else if (pathname.startsWith("/api")) {
|
||||
error(e.status, e.message);
|
||||
} else {
|
||||
redirect(302, "/auth/login?redirect=" + encodeURIComponent(pathname + search));
|
||||
redirect(302, withRedirectPath("/auth/login", pathname + search));
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (event.url.pathname === "/auth/login") {
|
||||
const redirectPath = event.url.searchParams.get("redirect");
|
||||
// Allow same-origin paths only
|
||||
redirect(302, redirectPath?.match(/^\/(?![/\\])/) ? redirectPath : "/home");
|
||||
redirect(302, getRedirectPath(event.url));
|
||||
}
|
||||
|
||||
return await resolve(event);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import argon2 from "argon2";
|
||||
import { ClientRepo, IntegrityError, SessionRepo, UserRepo } from "#lib/server/db/index.ts";
|
||||
import { ClientRepo, SessionRepo, UserRepo } from "#lib/server/db/index.ts";
|
||||
import env from "#lib/server/config.ts";
|
||||
import { issueSessionId } from "#lib/server/crypto.ts";
|
||||
import { AppError } from "#lib/server/errors.ts";
|
||||
import { AppError, invalidSessionError } from "#lib/server/errors.ts";
|
||||
import { createChallenge, verifyChallengeAnswer } from "./client.ts";
|
||||
|
||||
const MIN_PASSWORD_LENGTH = 8;
|
||||
@@ -36,7 +36,7 @@ export const changePassword = async (
|
||||
|
||||
const user = await UserRepo.getUser(userId);
|
||||
if (!user) {
|
||||
throw new AppError("INTERNAL_SERVER_ERROR", "Invalid session id");
|
||||
throw invalidSessionError();
|
||||
} else if (!(await argon2.verify(user.password, oldPassword))) {
|
||||
throw new AppError("FORBIDDEN", "Invalid password");
|
||||
}
|
||||
@@ -83,17 +83,12 @@ export const verifySessionUpgrade = async (
|
||||
) => {
|
||||
const challenge = await SessionRepo.consumeSessionUpgradeChallenge(challengeId, sessionId, ip);
|
||||
const client = await verifyChallengeAnswer(challenge, answerSig);
|
||||
await SessionRepo.upgradeSession(userId, sessionId, client.id, force);
|
||||
};
|
||||
|
||||
try {
|
||||
await SessionRepo.upgradeSession(userId, sessionId, client.id, force);
|
||||
} catch (e) {
|
||||
if (e instanceof IntegrityError) {
|
||||
if (e.message === "Session not found") {
|
||||
throw new AppError("INTERNAL_SERVER_ERROR", "Invalid challenge answer");
|
||||
} else if (e.message === "Session already exists") {
|
||||
throw new AppError("CONFLICT", "Already logged in");
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
export const cleanupExpiredSessions = async () => {
|
||||
await Promise.all([
|
||||
SessionRepo.cleanupExpiredSessions(),
|
||||
SessionRepo.cleanupExpiredSessionUpgradeChallenges(),
|
||||
]);
|
||||
};
|
||||
@@ -74,3 +74,7 @@ export const verifyClient = async (
|
||||
const client = await verifyChallengeAnswer(challenge, answerSig);
|
||||
await ClientRepo.setUserClientStateToPending(userId, client.id);
|
||||
};
|
||||
|
||||
export const cleanupExpiredChallenges = async () => {
|
||||
await ClientRepo.cleanupExpiredUserClientChallenges();
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ClientRepo, HskRepo, MekRepo } from "#lib/server/db/index.ts";
|
||||
import { verifySignature } from "#lib/server/crypto.ts";
|
||||
import { AppError } from "#lib/server/errors.ts";
|
||||
import { AppError, invalidSessionError } from "#lib/server/errors.ts";
|
||||
import { serializeMasterKeyWrapped } from "#lib/shared/crypto/index.ts";
|
||||
|
||||
export const getClientMeks = async (userId: number, clientId: number) => {
|
||||
return await MekRepo.getAllValidClientMeks(userId, clientId);
|
||||
@@ -15,10 +16,10 @@ const verifyClientEncMekSig = async (
|
||||
) => {
|
||||
const userClient = await ClientRepo.getUserClientWithDetails(userId, clientId);
|
||||
if (!userClient) {
|
||||
throw new AppError("INTERNAL_SERVER_ERROR", "Invalid session id");
|
||||
throw invalidSessionError();
|
||||
}
|
||||
|
||||
const data = JSON.stringify({ version, key: encMek });
|
||||
const data = serializeMasterKeyWrapped(encMek, version);
|
||||
return verifySignature(Buffer.from(data), encMekSig, userClient.sigPubKey);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { UserRepo } from "#lib/server/db/index.ts";
|
||||
import { AppError } from "#lib/server/errors.ts";
|
||||
import { invalidSessionError } from "#lib/server/errors.ts";
|
||||
|
||||
export const getUser = async (userId: number) => {
|
||||
const user = await UserRepo.getUser(userId);
|
||||
if (!user) {
|
||||
throw new AppError("INTERNAL_SERVER_ERROR", "Invalid session id");
|
||||
throw invalidSessionError();
|
||||
}
|
||||
return user;
|
||||
};
|
||||
Reference in new issue
Block a user