Merge branch 'develop' into feat-1714

This commit is contained in:
かっこかり
2024-07-18 18:58:36 +09:00
committed by GitHub
97 changed files with 1920 additions and 1128 deletions

View File

@@ -3,11 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as crypto from 'node:crypto';
import { IncomingMessage } from 'node:http';
import { Inject, Injectable } from '@nestjs/common';
import fastifyAccepts from '@fastify/accepts';
import httpSignature from '@peertube/http-signature';
import { verifyDigestHeader, parseRequestSignature } from '@misskey-dev/node-http-message-signatures';
import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
import accepts from 'accepts';
import vary from 'vary';
@@ -31,12 +30,17 @@ import { IActivity } from '@/core/activitypub/type.js';
import { isQuote, isRenote } from '@/misc/is-renote.js';
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
import type { FindOptionsWhere } from 'typeorm';
import { LoggerService } from '@/core/LoggerService.js';
import Logger from '@/logger.js';
const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
@Injectable()
export class ActivityPubServerService {
private logger: Logger;
private inboxLogger: Logger;
constructor(
@Inject(DI.config)
private config: Config,
@@ -71,8 +75,11 @@ export class ActivityPubServerService {
private queueService: QueueService,
private userKeypairService: UserKeypairService,
private queryService: QueryService,
private loggerService: LoggerService,
) {
//this.createServer = this.createServer.bind(this);
this.logger = this.loggerService.getLogger('server-ap', 'gray');
this.inboxLogger = this.logger.createSubLogger('inbox', 'gray');
}
@bindThis
@@ -100,70 +107,44 @@ export class ActivityPubServerService {
}
@bindThis
private inbox(request: FastifyRequest, reply: FastifyReply) {
let signature;
private async inbox(request: FastifyRequest, reply: FastifyReply) {
if (request.body == null) {
this.inboxLogger.warn('request body is empty');
reply.code(400);
return;
}
let signature: ReturnType<typeof parseRequestSignature>;
const verifyDigest = await verifyDigestHeader(request.raw, request.rawBody || '', true);
if (verifyDigest !== true) {
this.inboxLogger.warn('digest verification failed');
reply.code(401);
return;
}
try {
signature = httpSignature.parseRequest(request.raw, { 'headers': [] });
} catch (e) {
signature = parseRequestSignature(request.raw, {
requiredInputs: {
draft: ['(request-target)', 'digest', 'host', 'date'],
},
});
} catch (err) {
this.inboxLogger.warn('signature header parsing failed', { err });
if (typeof request.body === 'object' && 'signature' in request.body) {
// LD SignatureがあればOK
this.queueService.inbox(request.body as IActivity, null);
reply.code(202);
return;
}
this.inboxLogger.warn('signature header parsing failed and LD signature not found');
reply.code(401);
return;
}
if (signature.params.headers.indexOf('host') === -1
|| request.headers.host !== this.config.host) {
// Host not specified or not match.
reply.code(401);
return;
}
if (signature.params.headers.indexOf('digest') === -1) {
// Digest not found.
reply.code(401);
} else {
const digest = request.headers.digest;
if (typeof digest !== 'string') {
// Huh?
reply.code(401);
return;
}
const re = /^([a-zA-Z0-9\-]+)=(.+)$/;
const match = digest.match(re);
if (match == null) {
// Invalid digest
reply.code(401);
return;
}
const algo = match[1].toUpperCase();
const digestValue = match[2];
if (algo !== 'SHA-256') {
// Unsupported digest algorithm
reply.code(401);
return;
}
if (request.rawBody == null) {
// Bad request
reply.code(400);
return;
}
const hash = crypto.createHash('sha256').update(request.rawBody).digest('base64');
if (hash !== digestValue) {
// Invalid digest
reply.code(401);
return;
}
}
this.queueService.inbox(request.body as IActivity, signature);
reply.code(202);
}
@@ -640,7 +621,7 @@ export class ActivityPubServerService {
if (this.userEntityService.isLocalUser(user)) {
reply.header('Cache-Control', 'public, max-age=180');
this.setResponseType(request, reply);
return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair.publicKey)));
} else {
reply.code(400);
return;

View File

@@ -94,6 +94,13 @@ export class NodeinfoServerService {
localComments: 0,
},
metadata: {
/**
* '00': Draft, RSA only
* '01': Draft, Ed25519 suported
* '11': RFC 9421, Ed25519 supported
*/
httpMessageSignaturesImplementationLevel: '01',
nodeName: meta.name,
nodeDescription: meta.description,
nodeAdmins: [{

View File

@@ -56,7 +56,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const res = [] as [string, number][];
for (const job of jobs) {
const host = new URL(job.data.signature.keyId).host;
const signature = job.data.signature ? 'version' in job.data.signature ? job.data.signature.value : job.data.signature : null;
const host = signature ? Array.isArray(signature) ? 'TODO' : new URL(signature.keyId).host : new URL(job.data.activity.actor).host;
if (res.find(x => x[0] === host)) {
res.find(x => x[0] === host)![1]++;
} else {

View File

@@ -139,6 +139,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
timelineConfig = [
`homeTimeline:${me.id}`,
'localTimeline',
`localTimelineWithReplyTo:${me.id}`,
];
}

View File

@@ -6,12 +6,11 @@
import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { IdService } from '@/core/IdService.js';
import type { RenoteMutingsRepository } from '@/models/_.js';
import type { MiRenoteMuting } from '@/models/RenoteMuting.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { ApiError } from '../../error.js';
import { UserRenoteMutingService } from "@/core/UserRenoteMutingService.js";
import type { RenoteMutingsRepository } from '@/models/_.js';
export const meta = {
tags: ['account'],
@@ -62,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private renoteMutingsRepository: RenoteMutingsRepository,
private getterService: GetterService,
private idService: IdService,
private userRenoteMutingService: UserRenoteMutingService,
) {
super(meta, paramDef, async (ps, me) => {
const muter = me;
@@ -79,21 +78,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check if already muting
const exist = await this.renoteMutingsRepository.findOneBy({
muterId: muter.id,
muteeId: mutee.id,
const exist = await this.renoteMutingsRepository.exists({
where: {
muterId: muter.id,
muteeId: mutee.id,
},
});
if (exist != null) {
if (exist === true) {
throw new ApiError(meta.errors.alreadyMuting);
}
// Create mute
await this.renoteMutingsRepository.insert({
id: this.idService.gen(),
muterId: muter.id,
muteeId: mutee.id,
} as MiRenoteMuting);
await this.userRenoteMutingService.mute(muter, mutee);
});
}
}

View File

@@ -5,10 +5,11 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { RenoteMutingsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { ApiError } from '../../error.js';
import { UserRenoteMutingService } from "@/core/UserRenoteMutingService.js";
import type { RenoteMutingsRepository } from '@/models/_.js';
export const meta = {
tags: ['account'],
@@ -53,6 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private renoteMutingsRepository: RenoteMutingsRepository,
private getterService: GetterService,
private userRenoteMutingService: UserRenoteMutingService,
) {
super(meta, paramDef, async (ps, me) => {
const muter = me;
@@ -79,9 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
// Delete mute
await this.renoteMutingsRepository.delete({
id: exist.id,
});
await this.userRenoteMutingService.unmute([exist]);
});
}
}