Return 401s when logged out of more activitypub api endpoints (#750)
Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
This commit is contained in:
35
web/src/lib/util/activitypub_server_util.ts
Normal file
35
web/src/lib/util/activitypub_server_util.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { env } from '$env/dynamic/private';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
import { ClientResponseError } from 'pocketbase';
|
||||
|
||||
import type { Actor } from '$lib/models/activitypub/actor';
|
||||
import { isRemoteHandle } from '$lib/util/activitypub_util';
|
||||
|
||||
type GetActorOptions = {
|
||||
follows?: boolean;
|
||||
};
|
||||
|
||||
type ActorResponse = {
|
||||
actor: Actor;
|
||||
error?: string | null;
|
||||
};
|
||||
|
||||
export async function getActorResponseForHandle(event: RequestEvent, handle: string, options: GetActorOptions = {}): Promise<ActorResponse> {
|
||||
const origin = env.ORIGIN!;
|
||||
const remoteHandle = isRemoteHandle(handle, origin);
|
||||
if (remoteHandle && !event.locals.user) {
|
||||
throw new ClientResponseError({ status: 401, response: { message: "Unauthorized" } });
|
||||
}
|
||||
const searchParams = new URLSearchParams();
|
||||
if (options.follows) {
|
||||
searchParams.set("follows", "true");
|
||||
}
|
||||
const query = searchParams.toString();
|
||||
|
||||
const encodedHandle = encodeURIComponent(handle);
|
||||
const response: ActorResponse = await event.locals.pb.send(
|
||||
`/activitypub/actor?resource=acct:${encodedHandle}${query ? `&${query}` : ""}`,
|
||||
{ method: "GET", fetch: event.fetch },
|
||||
);
|
||||
return response;
|
||||
}
|
||||
@@ -3,8 +3,13 @@
|
||||
export function splitUsername(handle: string, localDomain?: string) {
|
||||
const cleaned = handle.replace(/^@/, "").trim();
|
||||
|
||||
let normalizedLocalDomain = localDomain;
|
||||
if (normalizedLocalDomain && normalizedLocalDomain.includes("://")) {
|
||||
normalizedLocalDomain = new URL(normalizedLocalDomain).hostname;
|
||||
}
|
||||
|
||||
if (!cleaned.includes("@")) {
|
||||
return [cleaned, localDomain];
|
||||
return [cleaned, normalizedLocalDomain];
|
||||
}
|
||||
|
||||
let [user, domain] = cleaned.split("@");
|
||||
@@ -12,6 +17,22 @@ export function splitUsername(handle: string, localDomain?: string) {
|
||||
return [user, domain]
|
||||
}
|
||||
|
||||
export function isRemoteHandle(handle: string, origin: string) {
|
||||
const [, domain] = splitUsername(handle, origin);
|
||||
if (!domain) {
|
||||
return false;
|
||||
}
|
||||
let normalizedDomain = domain;
|
||||
try {
|
||||
normalizedDomain = new URL(`http://${domain}`).hostname;
|
||||
} catch {
|
||||
normalizedDomain = domain.split(":")[0];
|
||||
}
|
||||
normalizedDomain = normalizedDomain.replace(/^www\./, "");
|
||||
const localHost = new URL(origin).hostname.replace(/^www\./, "");
|
||||
return normalizedDomain.toLowerCase() !== localHost.toLowerCase();
|
||||
}
|
||||
|
||||
export function handleFromRecordWithIRI(record: any) {
|
||||
if (!record.expand?.author) {
|
||||
throw new Error("object has no author info")
|
||||
@@ -23,4 +44,4 @@ export function handleFromRecordWithIRI(record: any) {
|
||||
const url = new URL(record.iri ?? "")
|
||||
|
||||
return `@${record.expand.author.preferred_username}@${url.hostname}`
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user