mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2025-03-04 07:18:50 -07:00
178 lines
4 KiB
TypeScript
178 lines
4 KiB
TypeScript
import { URL } from "url";
|
|
import httpSignature from "@peertube/http-signature";
|
|
import config from "@/config/index.js";
|
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
|
import { toPuny } from "@/misc/convert-host.js";
|
|
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
|
import { getApId } from "@/remote/activitypub/type.js";
|
|
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
|
import type { IncomingMessage } from "http";
|
|
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
|
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
|
|
|
export async function hasSignature(req: IncomingMessage): Promise<string> {
|
|
const meta = await fetchMeta();
|
|
const required = meta.secureMode || meta.privateMode;
|
|
|
|
try {
|
|
httpSignature.parseRequest(req, { headers: [] });
|
|
} catch (e) {
|
|
if (e instanceof Error && e.name === "MissingHeaderError") {
|
|
return required ? "missing" : "optional";
|
|
}
|
|
return "invalid";
|
|
}
|
|
return required ? "supplied" : "unneeded";
|
|
}
|
|
|
|
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
|
const meta = await fetchMeta();
|
|
if (meta.secureMode || meta.privateMode) {
|
|
let signature;
|
|
|
|
try {
|
|
signature = httpSignature.parseRequest(req, { headers: [] });
|
|
} catch (e) {
|
|
return 401;
|
|
}
|
|
|
|
const keyId = new URL(signature.keyId);
|
|
const host = toPuny(keyId.hostname);
|
|
|
|
if (await shouldBlockInstance(host, meta)) {
|
|
return 403;
|
|
}
|
|
|
|
if (
|
|
meta.privateMode &&
|
|
host !== config.host &&
|
|
!meta.allowedHosts.includes(host)
|
|
) {
|
|
return 403;
|
|
}
|
|
|
|
const keyIdLower = signature.keyId.toLowerCase();
|
|
if (keyIdLower.startsWith("acct:")) {
|
|
// Old keyId is no longer supported.
|
|
return 401;
|
|
}
|
|
|
|
const dbResolver = new DbResolver();
|
|
|
|
// HTTP-Signature keyIdを元にDBから取得
|
|
let authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId);
|
|
|
|
// keyIdでわからなければ、resolveしてみる
|
|
if (authUser == null) {
|
|
try {
|
|
keyId.hash = "";
|
|
authUser = await dbResolver.getAuthUserFromApId(
|
|
getApId(keyId.toString()),
|
|
);
|
|
} catch (e) {
|
|
// できなければ駄目
|
|
return 403;
|
|
}
|
|
}
|
|
|
|
// publicKey がなくても終了
|
|
if (authUser?.key == null) {
|
|
return 403;
|
|
}
|
|
|
|
// もう一回チェック
|
|
if (authUser.user.host !== host) {
|
|
return 403;
|
|
}
|
|
|
|
// HTTP-Signatureの検証
|
|
const httpSignatureValidated = httpSignature.verifySignature(
|
|
signature,
|
|
authUser.key.keyPem,
|
|
);
|
|
|
|
if (!httpSignatureValidated) {
|
|
return 403;
|
|
}
|
|
}
|
|
return 200;
|
|
}
|
|
|
|
export async function getSignatureUser(
|
|
req: IncomingMessage,
|
|
): Promise<{
|
|
user: CacheableRemoteUser;
|
|
key: UserPublickey | null;
|
|
} | null> {
|
|
let authUser;
|
|
const meta = await fetchMeta();
|
|
if (meta.secureMode || meta.privateMode) {
|
|
let signature;
|
|
|
|
try {
|
|
signature = httpSignature.parseRequest(req, { headers: [] });
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
|
|
const keyId = new URL(signature.keyId);
|
|
const host = toPuny(keyId.hostname);
|
|
|
|
if (await shouldBlockInstance(host, meta)) {
|
|
return null;
|
|
}
|
|
|
|
if (
|
|
meta.privateMode &&
|
|
host !== config.host &&
|
|
!meta.allowedHosts.includes(host)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const keyIdLower = signature.keyId.toLowerCase();
|
|
if (keyIdLower.startsWith("acct:")) {
|
|
// Old keyId is no longer supported.
|
|
return null;
|
|
}
|
|
|
|
const dbResolver = new DbResolver();
|
|
|
|
// HTTP-Signature keyIdを元にDBから取得
|
|
authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId);
|
|
|
|
// keyIdでわからなければ、resolveしてみる
|
|
if (!authUser) {
|
|
try {
|
|
keyId.hash = "";
|
|
authUser = await dbResolver.getAuthUserFromApId(
|
|
getApId(keyId.toString()),
|
|
);
|
|
} catch {
|
|
// できなければ駄目
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// publicKey がなくても終了
|
|
if (!authUser?.key) {
|
|
return null;
|
|
}
|
|
|
|
// もう一回チェック
|
|
if (authUser.user.host !== host) {
|
|
return null;
|
|
}
|
|
|
|
// HTTP-Signatureの検証
|
|
const httpSignatureValidated = httpSignature.verifySignature(
|
|
signature,
|
|
authUser.key.keyPem,
|
|
);
|
|
|
|
if (!httpSignatureValidated) {
|
|
return null;
|
|
}
|
|
}
|
|
return authUser;
|
|
}
|