aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-07-01 16:05:30 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-07-10 14:02:41 +0200
commitd95d15598847c7f020aa056e7e6e0c02d2bbf732 (patch)
treea8a593f1269688caf9e5f99559996f346290fec5 /server/controllers
parent72493e44e9b455a04c4f093ed6c6ffa300b98d8b (diff)
downloadPeerTube-d95d15598847c7f020aa056e7e6e0c02d2bbf732.tar.gz
PeerTube-d95d15598847c7f020aa056e7e6e0c02d2bbf732.tar.zst
PeerTube-d95d15598847c7f020aa056e7e6e0c02d2bbf732.zip
Use 3 tables to represent abuses
Diffstat (limited to 'server/controllers')
-rw-r--r--server/controllers/api/abuse.ts168
-rw-r--r--server/controllers/api/index.ts2
-rw-r--r--server/controllers/api/videos/abuse.ts109
3 files changed, 199 insertions, 80 deletions
diff --git a/server/controllers/api/abuse.ts b/server/controllers/api/abuse.ts
new file mode 100644
index 000000000..ee046cb3a
--- /dev/null
+++ b/server/controllers/api/abuse.ts
@@ -0,0 +1,168 @@
1import * as express from 'express'
2import { createAccountAbuse, createVideoAbuse, createVideoCommentAbuse } from '@server/lib/moderation'
3import { AbuseModel } from '@server/models/abuse/abuse'
4import { getServerActor } from '@server/models/application/application'
5import { AbuseCreate, abusePredefinedReasonsMap, AbuseState, UserRight } from '../../../shared'
6import { getFormattedObjects } from '../../helpers/utils'
7import { sequelizeTypescript } from '../../initializers/database'
8import {
9 abuseGetValidator,
10 abuseListValidator,
11 abuseReportValidator,
12 abusesSortValidator,
13 abuseUpdateValidator,
14 asyncMiddleware,
15 asyncRetryTransactionMiddleware,
16 authenticate,
17 ensureUserHasRight,
18 paginationValidator,
19 setDefaultPagination,
20 setDefaultSort
21} from '../../middlewares'
22import { AccountModel } from '../../models/account/account'
23
24const abuseRouter = express.Router()
25
26abuseRouter.get('/abuse',
27 authenticate,
28 ensureUserHasRight(UserRight.MANAGE_ABUSES),
29 paginationValidator,
30 abusesSortValidator,
31 setDefaultSort,
32 setDefaultPagination,
33 abuseListValidator,
34 asyncMiddleware(listAbuses)
35)
36abuseRouter.put('/:videoId/abuse/:id',
37 authenticate,
38 ensureUserHasRight(UserRight.MANAGE_ABUSES),
39 asyncMiddleware(abuseUpdateValidator),
40 asyncRetryTransactionMiddleware(updateAbuse)
41)
42abuseRouter.post('/:videoId/abuse',
43 authenticate,
44 asyncMiddleware(abuseReportValidator),
45 asyncRetryTransactionMiddleware(reportAbuse)
46)
47abuseRouter.delete('/:videoId/abuse/:id',
48 authenticate,
49 ensureUserHasRight(UserRight.MANAGE_ABUSES),
50 asyncMiddleware(abuseGetValidator),
51 asyncRetryTransactionMiddleware(deleteAbuse)
52)
53
54// ---------------------------------------------------------------------------
55
56export {
57 abuseRouter,
58
59 // FIXME: deprecated in 2.3. Remove these exports
60 listAbuses,
61 updateAbuse,
62 deleteAbuse,
63 reportAbuse
64}
65
66// ---------------------------------------------------------------------------
67
68async function listAbuses (req: express.Request, res: express.Response) {
69 const user = res.locals.oauth.token.user
70 const serverActor = await getServerActor()
71
72 const resultList = await AbuseModel.listForApi({
73 start: req.query.start,
74 count: req.query.count,
75 sort: req.query.sort,
76 id: req.query.id,
77 filter: 'video',
78 predefinedReason: req.query.predefinedReason,
79 search: req.query.search,
80 state: req.query.state,
81 videoIs: req.query.videoIs,
82 searchReporter: req.query.searchReporter,
83 searchReportee: req.query.searchReportee,
84 searchVideo: req.query.searchVideo,
85 searchVideoChannel: req.query.searchVideoChannel,
86 serverAccountId: serverActor.Account.id,
87 user
88 })
89
90 return res.json(getFormattedObjects(resultList.data, resultList.total))
91}
92
93async function updateAbuse (req: express.Request, res: express.Response) {
94 const abuse = res.locals.abuse
95
96 if (req.body.moderationComment !== undefined) abuse.moderationComment = req.body.moderationComment
97 if (req.body.state !== undefined) abuse.state = req.body.state
98
99 await sequelizeTypescript.transaction(t => {
100 return abuse.save({ transaction: t })
101 })
102
103 // Do not send the delete to other instances, we updated OUR copy of this video abuse
104
105 return res.type('json').status(204).end()
106}
107
108async function deleteAbuse (req: express.Request, res: express.Response) {
109 const abuse = res.locals.abuse
110
111 await sequelizeTypescript.transaction(t => {
112 return abuse.destroy({ transaction: t })
113 })
114
115 // Do not send the delete to other instances, we delete OUR copy of this video abuse
116
117 return res.type('json').status(204).end()
118}
119
120async function reportAbuse (req: express.Request, res: express.Response) {
121 const videoInstance = res.locals.videoAll
122 const commentInstance = res.locals.videoCommentFull
123 const accountInstance = res.locals.account
124
125 const body: AbuseCreate = req.body
126
127 const { id } = await sequelizeTypescript.transaction(async t => {
128 const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
129 const predefinedReasons = body.predefinedReasons?.map(r => abusePredefinedReasonsMap[r])
130
131 const baseAbuse = {
132 reporterAccountId: reporterAccount.id,
133 reason: body.reason,
134 state: AbuseState.PENDING,
135 predefinedReasons
136 }
137
138 if (body.video) {
139 return createVideoAbuse({
140 baseAbuse,
141 videoInstance,
142 reporterAccount,
143 transaction: t,
144 startAt: body.video.startAt,
145 endAt: body.video.endAt
146 })
147 }
148
149 if (body.comment) {
150 return createVideoCommentAbuse({
151 baseAbuse,
152 commentInstance,
153 reporterAccount,
154 transaction: t
155 })
156 }
157
158 // Account report
159 return createAccountAbuse({
160 baseAbuse,
161 accountInstance,
162 reporterAccount,
163 transaction: t
164 })
165 })
166
167 return res.json({ abuse: { id } })
168}
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts
index c334a26b4..eda9e04d1 100644
--- a/server/controllers/api/index.ts
+++ b/server/controllers/api/index.ts
@@ -3,6 +3,7 @@ import * as express from 'express'
3import * as RateLimit from 'express-rate-limit' 3import * as RateLimit from 'express-rate-limit'
4import { badRequest } from '../../helpers/express-utils' 4import { badRequest } from '../../helpers/express-utils'
5import { CONFIG } from '../../initializers/config' 5import { CONFIG } from '../../initializers/config'
6import { abuseRouter } from './abuse'
6import { accountsRouter } from './accounts' 7import { accountsRouter } from './accounts'
7import { bulkRouter } from './bulk' 8import { bulkRouter } from './bulk'
8import { configRouter } from './config' 9import { configRouter } from './config'
@@ -32,6 +33,7 @@ const apiRateLimiter = RateLimit({
32apiRouter.use(apiRateLimiter) 33apiRouter.use(apiRateLimiter)
33 34
34apiRouter.use('/server', serverRouter) 35apiRouter.use('/server', serverRouter)
36apiRouter.use('/abuses', abuseRouter)
35apiRouter.use('/bulk', bulkRouter) 37apiRouter.use('/bulk', bulkRouter)
36apiRouter.use('/oauth-clients', oauthClientsRouter) 38apiRouter.use('/oauth-clients', oauthClientsRouter)
37apiRouter.use('/config', configRouter) 39apiRouter.use('/config', configRouter)
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index ab2074459..b92a66360 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -1,9 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight, VideoAbuseCreate, VideoAbuseState, VideoAbuse, videoAbusePredefinedReasonsMap } from '../../../../shared' 2import { AbuseModel } from '@server/models/abuse/abuse'
3import { logger } from '../../../helpers/logger' 3import { getServerActor } from '@server/models/application/application'
4import { AbuseCreate, UserRight, VideoAbuseCreate } from '../../../../shared'
4import { getFormattedObjects } from '../../../helpers/utils' 5import { getFormattedObjects } from '../../../helpers/utils'
5import { sequelizeTypescript } from '../../../initializers/database'
6import { 6import {
7 abusesSortValidator,
7 asyncMiddleware, 8 asyncMiddleware,
8 asyncRetryTransactionMiddleware, 9 asyncRetryTransactionMiddleware,
9 authenticate, 10 authenticate,
@@ -12,28 +13,21 @@ import {
12 setDefaultPagination, 13 setDefaultPagination,
13 setDefaultSort, 14 setDefaultSort,
14 videoAbuseGetValidator, 15 videoAbuseGetValidator,
16 videoAbuseListValidator,
15 videoAbuseReportValidator, 17 videoAbuseReportValidator,
16 videoAbusesSortValidator, 18 videoAbuseUpdateValidator
17 videoAbuseUpdateValidator,
18 videoAbuseListValidator
19} from '../../../middlewares' 19} from '../../../middlewares'
20import { AccountModel } from '../../../models/account/account' 20import { deleteAbuse, reportAbuse, updateAbuse } from '../abuse'
21import { VideoAbuseModel } from '../../../models/video/video-abuse' 21
22import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' 22// FIXME: deprecated in 2.3. Remove this controller
23import { Notifier } from '../../../lib/notifier'
24import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
25import { MVideoAbuseAccountVideo } from '../../../types/models/video'
26import { getServerActor } from '@server/models/application/application'
27import { MAccountDefault } from '@server/types/models'
28 23
29const auditLogger = auditLoggerFactory('abuse')
30const abuseVideoRouter = express.Router() 24const abuseVideoRouter = express.Router()
31 25
32abuseVideoRouter.get('/abuse', 26abuseVideoRouter.get('/abuse',
33 authenticate, 27 authenticate,
34 ensureUserHasRight(UserRight.MANAGE_VIDEO_ABUSES), 28 ensureUserHasRight(UserRight.MANAGE_ABUSES),
35 paginationValidator, 29 paginationValidator,
36 videoAbusesSortValidator, 30 abusesSortValidator,
37 setDefaultSort, 31 setDefaultSort,
38 setDefaultPagination, 32 setDefaultPagination,
39 videoAbuseListValidator, 33 videoAbuseListValidator,
@@ -41,7 +35,7 @@ abuseVideoRouter.get('/abuse',
41) 35)
42abuseVideoRouter.put('/:videoId/abuse/:id', 36abuseVideoRouter.put('/:videoId/abuse/:id',
43 authenticate, 37 authenticate,
44 ensureUserHasRight(UserRight.MANAGE_VIDEO_ABUSES), 38 ensureUserHasRight(UserRight.MANAGE_ABUSES),
45 asyncMiddleware(videoAbuseUpdateValidator), 39 asyncMiddleware(videoAbuseUpdateValidator),
46 asyncRetryTransactionMiddleware(updateVideoAbuse) 40 asyncRetryTransactionMiddleware(updateVideoAbuse)
47) 41)
@@ -52,7 +46,7 @@ abuseVideoRouter.post('/:videoId/abuse',
52) 46)
53abuseVideoRouter.delete('/:videoId/abuse/:id', 47abuseVideoRouter.delete('/:videoId/abuse/:id',
54 authenticate, 48 authenticate,
55 ensureUserHasRight(UserRight.MANAGE_VIDEO_ABUSES), 49 ensureUserHasRight(UserRight.MANAGE_ABUSES),
56 asyncMiddleware(videoAbuseGetValidator), 50 asyncMiddleware(videoAbuseGetValidator),
57 asyncRetryTransactionMiddleware(deleteVideoAbuse) 51 asyncRetryTransactionMiddleware(deleteVideoAbuse)
58) 52)
@@ -69,11 +63,12 @@ async function listVideoAbuses (req: express.Request, res: express.Response) {
69 const user = res.locals.oauth.token.user 63 const user = res.locals.oauth.token.user
70 const serverActor = await getServerActor() 64 const serverActor = await getServerActor()
71 65
72 const resultList = await VideoAbuseModel.listForApi({ 66 const resultList = await AbuseModel.listForApi({
73 start: req.query.start, 67 start: req.query.start,
74 count: req.query.count, 68 count: req.query.count,
75 sort: req.query.sort, 69 sort: req.query.sort,
76 id: req.query.id, 70 id: req.query.id,
71 filter: 'video',
77 predefinedReason: req.query.predefinedReason, 72 predefinedReason: req.query.predefinedReason,
78 search: req.query.search, 73 search: req.query.search,
79 state: req.query.state, 74 state: req.query.state,
@@ -90,74 +85,28 @@ async function listVideoAbuses (req: express.Request, res: express.Response) {
90} 85}
91 86
92async function updateVideoAbuse (req: express.Request, res: express.Response) { 87async function updateVideoAbuse (req: express.Request, res: express.Response) {
93 const videoAbuse = res.locals.videoAbuse 88 return updateAbuse(req, res)
94
95 if (req.body.moderationComment !== undefined) videoAbuse.moderationComment = req.body.moderationComment
96 if (req.body.state !== undefined) videoAbuse.state = req.body.state
97
98 await sequelizeTypescript.transaction(t => {
99 return videoAbuse.save({ transaction: t })
100 })
101
102 // Do not send the delete to other instances, we updated OUR copy of this video abuse
103
104 return res.type('json').status(204).end()
105} 89}
106 90
107async function deleteVideoAbuse (req: express.Request, res: express.Response) { 91async function deleteVideoAbuse (req: express.Request, res: express.Response) {
108 const videoAbuse = res.locals.videoAbuse 92 return deleteAbuse(req, res)
109
110 await sequelizeTypescript.transaction(t => {
111 return videoAbuse.destroy({ transaction: t })
112 })
113
114 // Do not send the delete to other instances, we delete OUR copy of this video abuse
115
116 return res.type('json').status(204).end()
117} 93}
118 94
119async function reportVideoAbuse (req: express.Request, res: express.Response) { 95async function reportVideoAbuse (req: express.Request, res: express.Response) {
120 const videoInstance = res.locals.videoAll 96 const oldBody = req.body as VideoAbuseCreate
121 const body: VideoAbuseCreate = req.body
122 let reporterAccount: MAccountDefault
123 let videoAbuseJSON: VideoAbuse
124
125 const videoAbuseInstance = await sequelizeTypescript.transaction(async t => {
126 reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
127 const predefinedReasons = body.predefinedReasons?.map(r => videoAbusePredefinedReasonsMap[r])
128
129 const abuseToCreate = {
130 reporterAccountId: reporterAccount.id,
131 reason: body.reason,
132 videoId: videoInstance.id,
133 state: VideoAbuseState.PENDING,
134 predefinedReasons,
135 startAt: body.startAt,
136 endAt: body.endAt
137 }
138
139 const videoAbuseInstance: MVideoAbuseAccountVideo = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
140 videoAbuseInstance.Video = videoInstance
141 videoAbuseInstance.Account = reporterAccount
142
143 // We send the video abuse to the origin server
144 if (videoInstance.isOwned() === false) {
145 await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance, t)
146 }
147 97
148 videoAbuseJSON = videoAbuseInstance.toFormattedJSON() 98 req.body = {
149 auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseJSON)) 99 accountId: res.locals.videoAll.VideoChannel.accountId,
150 100
151 return videoAbuseInstance 101 reason: oldBody.reason,
152 }) 102 predefinedReasons: oldBody.predefinedReasons,
153 103
154 Notifier.Instance.notifyOnNewVideoAbuse({ 104 video: {
155 videoAbuse: videoAbuseJSON, 105 id: res.locals.videoAll.id,
156 videoAbuseInstance, 106 startAt: oldBody.startAt,
157 reporter: reporterAccount.Actor.getIdentifier() 107 endAt: oldBody.endAt
158 }) 108 }
159 109 } as AbuseCreate
160 logger.info('Abuse report for video "%s" created.', videoInstance.name)
161 110
162 return res.json({ videoAbuse: videoAbuseJSON }).end() 111 return reportAbuse(req, res)
163} 112}