Skip to content

Commit

Permalink
AP Note/Userあたりを抽象化
Browse files Browse the repository at this point in the history
  • Loading branch information
mei23 committed Apr 18, 2020
1 parent 253e25f commit 524f1f9
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 146 deletions.
32 changes: 7 additions & 25 deletions src/queue/processors/inbox.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Bull from 'bull';
import * as httpSignature from 'http-signature';
import parseAcct from '../../misc/acct/parse';
import User, { IRemoteUser } from '../../models/user';
import { IRemoteUser } from '../../models/user';
import perform from '../../remote/activitypub/perform';
import { resolvePerson } from '../../remote/activitypub/models/person';
import { toUnicode } from 'punycode';
Expand All @@ -15,6 +14,7 @@ import { getApId } from '../../remote/activitypub/type';
import { UpdateInstanceinfo } from '../../services/update-instanceinfo';
import { isBlockedHost } from '../../misc/instance-info';
import { InboxJobData } from '..';
import ApResolver from '../../remote/activitypub/ap-resolver';

const logger = new Logger('inbox');

Expand All @@ -23,6 +23,8 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
const signature = job.data.signature;
const activity = job.data.activity;

const apResolver = new ApResolver();

//#region Log
const info = Object.assign({}, activity);
delete info['@context'];
Expand All @@ -31,27 +33,10 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
//#endregion

const keyIdLower = signature.keyId.toLowerCase();
let user: IRemoteUser;
let user: IRemoteUser | null;

if (keyIdLower.startsWith('acct:')) {
const { username, host } = parseAcct(keyIdLower.slice('acct:'.length));
if (host === null) {
return `skip: request was made by local user: @${username}`;
}

// アクティビティ内のホストの検証
try {
ValidateActivity(activity, host);
} catch (e) {
return `skip: host validation failed ${e.message}`;
}

// ブロックしてたら中断
if (await isBlockedHost(host)) {
return `skip: Blocked instance: ${host}`;
}

user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
return `skip: acct keyId is no longer supported`;
} else {
// アクティビティ内のホストの検証
const host = toUnicode(new URL(signature.keyId).hostname.toLowerCase());
Expand All @@ -66,10 +51,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
return `skip: Blocked instance: ${host}`;
}

user = await User.findOne({
host: { $ne: null },
'publicKey.id': signature.keyId
}) as IRemoteUser;
user = await apResolver.getRemoteUserFromKeyId(signature.keyId);
}

// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
Expand Down
98 changes: 98 additions & 0 deletions src/remote/activitypub/ap-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as mongo from 'mongodb';
import config from '../../config';
import User, { IUser, IRemoteUser } from '../../models/user';
import Note, { INote } from '../../models/note';
import { IObject, getApId } from './type';
import * as escapeRegexp from 'escape-regexp';
import { inspect } from 'util';

export default class ApResolver {
constructor() {
}

/**
* AP Note => Misskey Note in DB
* @param object AP object
*/
public async getNoteFromObject(object: string | IObject): Promise<INote | null> {
const parsed = this.parseObjectUri(object);

if (parsed.id) {
return (await Note.findOne({
_id: new mongo.ObjectID(parsed.id),
deletedAt: { $exists: false }
})) || null;
}

if (parsed.uri) {
return (await Note.findOne({
uri: parsed.uri,
deletedAt: { $exists: false }
})) || null;
}

return null;
}

public async getRemoteUserFromKeyId(keyId: string): Promise<IRemoteUser | null> {
const user = await User.findOne({
host: { $ne: null },
'publicKey.id': keyId,
deletedAt: { $exists: false }
}) as IRemoteUser;

return user || null;
}

/**
* AP Person => Misskey User in DB
* @param object AP Object
*/
public async getUserFromObject(object: string | IObject): Promise<IUser | null> {
const parsed = this.parseObjectUri(object);

console.log(inspect(parsed));
if (parsed.id) {
return (await User.findOne({
_id: new mongo.ObjectID(parsed.id),
deletedAt: { $exists: false }
})) || null;
}

if (parsed.uri) {
return (await User.findOne({
uri: parsed.uri,
deletedAt: { $exists: false }
})) || null;
}

return null;
}

public parseObjectUri(object: string | IObject): UriParseResult {
const uri = getApId(object);

const localRegex = new RegExp('^' + escapeRegexp(config.url) + '/' + '(\\w+)' + '/' + '(\\w+)');
const matchLocal = uri.match(localRegex);

if (matchLocal) {
return {
type: matchLocal[1],
id: matchLocal[2]
};
} else {
return {
uri
};
}
}
}

type UriParseResult = {
/** id in DB (local object only) */
id?: string;
/** uri in DB (remote object only) */
uri?: string;
/** hint of type (local object only, ex: notes, users) */
type?: string
};
22 changes: 8 additions & 14 deletions src/remote/activitypub/kernel/accept/follow.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../../models/user';
import config from '../../../../config';
import { IRemoteUser, isLocalUser } from '../../../../models/user';
import accept from '../../../../services/following/requests/accept';
import { IFollow, getApId } from '../../type';
import { IFollow } from '../../type';
import ApResolver from '../../ap-resolver';

export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
const id = getApId(activity.actor);
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある

if (!id.startsWith(config.url + '/')) {
return `skip: accept target is no a local user.`;
}

const follower = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});
const apResolver = new ApResolver();
const follower = await apResolver.getUserFromObject(activity.actor);

if (follower == null) {
return `skip: follower not found`;
}

if (follower.host != null) {
throw new Error('フォローリクエストしたユーザーはローカルユーザーではありません');
if (!isLocalUser(follower)) {
return `skip: follower is not a local user`;
}

await accept(actor, follower);
Expand Down
29 changes: 8 additions & 21 deletions src/remote/activitypub/kernel/block/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../../models/user';
import config from '../../../../config';
import { IBlock, getApId } from '../../type';
import { IRemoteUser, isLocalUser } from '../../../../models/user';
import { IBlock } from '../../type';
import block from '../../../../services/blocking/create';
import { apLogger } from '../../logger';

const logger = apLogger;
import ApResolver from '../../ap-resolver';

export default async (actor: IRemoteUser, activity: IBlock): Promise<string> => {
const id = getApId(activity.object);

const uri = getApId(activity);

logger.info(`Block: ${uri}`);

if (!id.startsWith(config.url + '/')) {
return `skip: blockee is not a local user`;
}
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず

const blockee = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});
const apResolver = new ApResolver();
const blockee = await apResolver.getUserFromObject(activity.object);

if (blockee === null) {
if (blockee == null) {
return `skip: blockee not found`;
}

if (blockee.host != null) {
if (!isLocalUser(blockee)) {
return `skip: ブロックしようとしているユーザーはローカルユーザーではありません`;
}

Expand Down
5 changes: 3 additions & 2 deletions src/remote/activitypub/kernel/delete/note.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Note from '../../../../models/note';
import { IRemoteUser } from '../../../../models/user';
import deleteNode from '../../../../services/note/delete';
import { apLogger } from '../../logger';
import { getApLock } from '../../../../misc/app-lock';
import ApResolver from '../../ap-resolver';

const logger = apLogger;

Expand All @@ -12,7 +12,8 @@ export default async function(actor: IRemoteUser, uri: string): Promise<string>
const unlock = await getApLock(uri);

try {
const note = await Note.findOne({ uri });
const apResolver = new ApResolver();
const note = await apResolver.getNoteFromObject(uri);

if (note == null) {
return 'note not found';
Expand Down
20 changes: 6 additions & 14 deletions src/remote/activitypub/kernel/follow.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../models/user';
import config from '../../../config';
import { IRemoteUser, isLocalUser } from '../../../models/user';
import follow from '../../../services/following/create';
import { IFollow } from '../type';
import ApResolver from '../ap-resolver';

export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
const apResolver = new ApResolver();
const followee = await apResolver.getUserFromObject(activity.object);

if (!id.startsWith(config.url + '/')) {
return `skip: invalid target`;
}

const followee = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});

if (followee === null) {
if (followee == null) {
return `skip: followee not found`;
}

if (followee.host != null) {
if (!isLocalUser(followee)) {
return `skip: フォローしようとしているユーザーはローカルユーザーではありません`;
}

Expand Down
22 changes: 8 additions & 14 deletions src/remote/activitypub/kernel/reject/follow.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../../models/user';
import config from '../../../../config';
import { IRemoteUser, isLocalUser } from '../../../../models/user';
import reject from '../../../../services/following/requests/reject';
import { IFollow, getApId } from '../../type';
import { IFollow } from '../../type';
import ApResolver from '../../ap-resolver';

export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
const id = getApId(activity.actor);
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある

if (!id.startsWith(config.url + '/')) {
return `skip: reject target is not a local user`;
}

const follower = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});
const apResolver = new ApResolver();
const follower = await apResolver.getUserFromObject(activity.actor);

if (follower == null) {
return `skip: follower is not found`;
return `skip: follower not found`;
}

if (follower.host != null) {
if (!isLocalUser(follower)) {
return `skip: follower is not a local user`;
}

Expand Down
27 changes: 6 additions & 21 deletions src/remote/activitypub/kernel/undo/block.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../../models/user';
import config from '../../../../config';
import { IRemoteUser, isLocalUser } from '../../../../models/user';
import { IBlock } from '../../type';
import unblock from '../../../../services/blocking/delete';
import { apLogger } from '../../logger';

const logger = apLogger;
import ApResolver from '../../ap-resolver';

export default async (actor: IRemoteUser, activity: IBlock): Promise<string> => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;

const uri = activity.id || activity;

logger.info(`UnBlock: ${uri}`);

if (!id.startsWith(config.url + '/')) {
return `skip: invalid target`;
}

const blockee = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});
const apResolver = new ApResolver();
const blockee = await apResolver.getUserFromObject(activity.object);

if (blockee === null) {
if (blockee == null) {
return `skip: blockee not found`;
}

if (blockee.host != null) {
if (!isLocalUser(blockee)) {
return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`;
}

Expand Down

0 comments on commit 524f1f9

Please sign in to comment.