mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-05 22:56:00 +02:00
Merge commit from fork
* Tighten security in `HashtagChannel` * Add isNoteVisibleForMe in stream channel Co-Authored-By: Julia Johannesen <julia@insertdomain.name> * Tighten note visibility checks in WebSocket (No.1) * refactor * Fix main channel Co-Authored-By: Julia Johannesen <julia@insertdomain.name> * fix typo * fix missing lockdown (requireSigninToViewContents) checks * fix(backend): streamingでのロックダウン挙動修正 * fix: 引用リノートを無条件で隠していた問題を修正 * fix: 引用リノートを単純にリノート場合に内容が見えることがある問題を修正 * refac * fix * fix * fix * Update docs --------- Co-authored-by: Julia Johannesen <julia@insertdomain.name> Co-authored-by: KanariKanaru <93921745+kanarikanaru@users.noreply.github.com>
This commit is contained in:
@@ -259,7 +259,7 @@ export class QueryService {
|
||||
|
||||
@bindThis
|
||||
public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
|
||||
// This code must always be synchronized with the checks in Notes.isVisibleForMe.
|
||||
// This code must always be synchronized with the checks in NoteEntityService.isVisibleForMe and Stream abstract class Channel.isNoteVisibleForMe.
|
||||
if (me == null) {
|
||||
q.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
|
||||
@@ -17,6 +17,7 @@ import { DebounceLoader } from '@/misc/loader.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { shouldHideNoteByTime } from '@/misc/should-hide-note-by-time.js';
|
||||
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import type { OnModuleInit } from '@nestjs/common';
|
||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||
import type { ReactionService } from '../ReactionService.js';
|
||||
@@ -66,6 +67,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||
private reactionService: ReactionService;
|
||||
private reactionsBufferingService: ReactionsBufferingService;
|
||||
private idService: IdService;
|
||||
private cacheService: CacheService;
|
||||
private noteLoader = new DebounceLoader(this.findNoteOrFail);
|
||||
|
||||
constructor(
|
||||
@@ -101,6 +103,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||
//private reactionService: ReactionService,
|
||||
//private reactionsBufferingService: ReactionsBufferingService,
|
||||
//private idService: IdService,
|
||||
//private cacheService: CacheService,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -111,6 +114,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||
this.reactionService = this.moduleRef.get('ReactionService');
|
||||
this.reactionsBufferingService = this.moduleRef.get('ReactionsBufferingService');
|
||||
this.idService = this.moduleRef.get('IdService');
|
||||
this.cacheService = this.moduleRef.get('CacheService');
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -125,75 +129,65 @@ export class NoteEntityService implements OnModuleInit {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<void> {
|
||||
if (meId === packedNote.userId) return;
|
||||
|
||||
public async shouldHideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<boolean> {
|
||||
if (meId === packedNote.userId) return false;
|
||||
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
|
||||
let hide = false;
|
||||
|
||||
if (packedNote.user.requireSigninToViewContents && meId == null) {
|
||||
hide = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hide) {
|
||||
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
|
||||
if (shouldHideNoteByTime(hiddenBefore, packedNote.createdAt)) {
|
||||
hide = true;
|
||||
}
|
||||
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
|
||||
if (shouldHideNoteByTime(hiddenBefore, packedNote.createdAt)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// visibility が specified かつ自分が指定されていなかったら非表示
|
||||
if (!hide) {
|
||||
if (packedNote.visibility === 'specified') {
|
||||
if (meId == null) {
|
||||
hide = true;
|
||||
} else {
|
||||
// 指定されているかどうか
|
||||
const specified = packedNote.visibleUserIds!.some(id => meId === id);
|
||||
if (packedNote.visibility === 'specified') {
|
||||
if (meId == null) {
|
||||
return true;
|
||||
} else {
|
||||
// 指定されているかどうか
|
||||
const specified = packedNote.visibleUserIds!.some(id => meId === id);
|
||||
|
||||
if (!specified) {
|
||||
hide = true;
|
||||
}
|
||||
if (!specified) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
||||
if (!hide) {
|
||||
if (packedNote.visibility === 'followers') {
|
||||
if (meId == null) {
|
||||
hide = true;
|
||||
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
|
||||
// 自分の投稿に対するリプライ
|
||||
hide = false;
|
||||
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
|
||||
// 自分へのメンション
|
||||
hide = false;
|
||||
} else {
|
||||
// フォロワーかどうか
|
||||
// TODO: 当関数呼び出しごとにクエリが走るのは重そうだからなんとかする
|
||||
const isFollowing = await this.followingsRepository.exists({
|
||||
where: {
|
||||
followeeId: packedNote.userId,
|
||||
followerId: meId,
|
||||
},
|
||||
});
|
||||
|
||||
hide = !isFollowing;
|
||||
if (packedNote.visibility === 'followers') {
|
||||
if (meId == null) {
|
||||
return true;
|
||||
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
|
||||
// 自分の投稿に対するリプライ
|
||||
return false;
|
||||
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
|
||||
// 自分へのメンション
|
||||
return false;
|
||||
} else {
|
||||
// フォロワーかどうか
|
||||
const followings = await this.cacheService.userFollowingsCache.fetch(meId);
|
||||
if (!Object.hasOwn(followings, packedNote.userId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hide) {
|
||||
packedNote.visibleUserIds = undefined;
|
||||
packedNote.fileIds = [];
|
||||
packedNote.files = [];
|
||||
packedNote.text = null;
|
||||
packedNote.poll = undefined;
|
||||
packedNote.cw = null;
|
||||
packedNote.isHidden = true;
|
||||
// TODO: hiddenReason みたいなのを提供しても良さそう
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public hideNote(packedNote: Packed<'Note'>): void {
|
||||
packedNote.visibleUserIds = undefined;
|
||||
packedNote.fileIds = [];
|
||||
packedNote.files = [];
|
||||
packedNote.text = null;
|
||||
packedNote.poll = undefined;
|
||||
packedNote.cw = null;
|
||||
packedNote.isHidden = true;
|
||||
// TODO: hiddenReason みたいなのを提供しても良さそう
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -278,7 +272,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||
|
||||
@bindThis
|
||||
public async isVisibleForMe(note: MiNote, meId: MiUser['id'] | null): Promise<boolean> {
|
||||
// This code must always be synchronized with the checks in generateVisibilityQuery.
|
||||
// This code must always be synchronized with the checks in QueryService.generateVisibilityQuery.
|
||||
// visibility が specified かつ自分が指定されていなかったら非表示
|
||||
if (note.visibility === 'specified') {
|
||||
if (meId == null) {
|
||||
@@ -468,8 +462,8 @@ export class NoteEntityService implements OnModuleInit {
|
||||
|
||||
this.treatVisibility(packed);
|
||||
|
||||
if (!opts.skipHide) {
|
||||
await this.hideNote(packed, meId);
|
||||
if (!opts.skipHide && await this.shouldHideNote(packed, meId)) {
|
||||
this.hideNote(packed);
|
||||
}
|
||||
|
||||
return packed;
|
||||
|
||||
Reference in New Issue
Block a user