mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
refactor(federation): ♻️ Allow ActivityPub bridge requests to omit all signature headers, including x-signed-by
This commit is contained in:
parent
80b5184d6a
commit
b55237cdc8
|
|
@ -25,7 +25,11 @@ export const schemas = {
|
||||||
header: z.object({
|
header: z.object({
|
||||||
"x-signature": z.string().optional(),
|
"x-signature": z.string().optional(),
|
||||||
"x-nonce": z.string().optional(),
|
"x-nonce": z.string().optional(),
|
||||||
"x-signed-by": z.string().url().or(z.string().startsWith("instance ")),
|
"x-signed-by": z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.or(z.string().startsWith("instance "))
|
||||||
|
.optional(),
|
||||||
authorization: z.string().optional(),
|
authorization: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
body: z.any(),
|
body: z.any(),
|
||||||
|
|
@ -111,6 +115,32 @@ export default apiRoute((app) =>
|
||||||
const logger = getLogger(["federation", "inbox"]);
|
const logger = getLogger(["federation", "inbox"]);
|
||||||
const body: Entity = await context.req.valid("json");
|
const body: Entity = await context.req.valid("json");
|
||||||
|
|
||||||
|
if (authorization) {
|
||||||
|
const processor = new InboxProcessor(
|
||||||
|
context,
|
||||||
|
body,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
signature,
|
||||||
|
nonce,
|
||||||
|
authorization,
|
||||||
|
},
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
return await processor.process();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not potentially from bridge, check for required headers
|
||||||
|
if (!(signature && nonce && signedBy)) {
|
||||||
|
return context.json(
|
||||||
|
{
|
||||||
|
error: "Missing required headers: x-signature, x-nonce, or x-signed-by",
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const sender = await User.resolve(signedBy);
|
const sender = await User.resolve(signedBy);
|
||||||
|
|
||||||
if (!(sender || signedBy.startsWith("instance "))) {
|
if (!(sender || signedBy.startsWith("instance "))) {
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ export class InboxProcessor {
|
||||||
*
|
*
|
||||||
* @param context Hono request context.
|
* @param context Hono request context.
|
||||||
* @param body Entity JSON body.
|
* @param body Entity JSON body.
|
||||||
* @param senderInstance Sender of the request's instance (from X-Signed-By header).
|
* @param senderInstance Sender of the request's instance (from X-Signed-By header). Null if request is from a bridge.
|
||||||
* @param headers Various request headers.
|
* @param headers Various request headers.
|
||||||
* @param logger LogTape logger instance.
|
* @param logger LogTape logger instance.
|
||||||
* @param requestIp Request IP address. Grabs it from the Hono context if not provided.
|
* @param requestIp Request IP address. Grabs it from the Hono context if not provided.
|
||||||
|
|
@ -78,7 +78,7 @@ export class InboxProcessor {
|
||||||
public constructor(
|
public constructor(
|
||||||
private context: Context,
|
private context: Context,
|
||||||
private body: Entity,
|
private body: Entity,
|
||||||
private senderInstance: Instance,
|
private senderInstance: Instance | null,
|
||||||
private headers: {
|
private headers: {
|
||||||
signature?: string;
|
signature?: string;
|
||||||
nonce?: string;
|
nonce?: string;
|
||||||
|
|
@ -94,9 +94,9 @@ export class InboxProcessor {
|
||||||
* @returns {Promise<boolean>} - Whether the signature is valid.
|
* @returns {Promise<boolean>} - Whether the signature is valid.
|
||||||
*/
|
*/
|
||||||
private async isSignatureValid(): Promise<boolean> {
|
private async isSignatureValid(): Promise<boolean> {
|
||||||
if (!this.senderInstance.data.publicKey?.key) {
|
if (!this.senderInstance?.data.publicKey?.key) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Instance ${this.senderInstance.data.baseUrl} has no public key stored in database`,
|
`Instance ${this.senderInstance?.data.baseUrl} has no public key stored in database`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,7 +196,10 @@ export class InboxProcessor {
|
||||||
public async process(): Promise<
|
public async process(): Promise<
|
||||||
(Response & TypedResponse<{ error: string }, 500, "json">) | Response
|
(Response & TypedResponse<{ error: string }, 500, "json">) | Response
|
||||||
> {
|
> {
|
||||||
if (isDefederated(this.senderInstance.data.baseUrl)) {
|
if (
|
||||||
|
this.senderInstance &&
|
||||||
|
isDefederated(this.senderInstance.data.baseUrl)
|
||||||
|
) {
|
||||||
// Return 201 to avoid
|
// Return 201 to avoid
|
||||||
// 1. Leaking defederated instance information
|
// 1. Leaking defederated instance information
|
||||||
// 2. Preventing the sender from thinking the message was not delivered and retrying
|
// 2. Preventing the sender from thinking the message was not delivered and retrying
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue