aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorAurélien Bertron <aurelienbertron@gmail.com>2018-07-31 14:04:26 +0200
committerChocobozzz <me@florianbigard.com>2018-07-31 15:40:29 +0200
commit80e36cd9facb56b330be3e4f1c5ba253cc78c308 (patch)
tree807d8a642ae99ec3f05597e19ebe1ca5dc849582 /server
parent59390818384baa0ffc0cb71af2e67350c6b39172 (diff)
downloadPeerTube-80e36cd9facb56b330be3e4f1c5ba253cc78c308.tar.gz
PeerTube-80e36cd9facb56b330be3e4f1c5ba253cc78c308.tar.zst
PeerTube-80e36cd9facb56b330be3e4f1c5ba253cc78c308.zip
Add audit logs in various modules
- Videos - Videos comments - Users - Videos channels - Videos abuses - Custom config
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/config.ts16
-rw-r--r--server/controllers/api/users.ts49
-rw-r--r--server/controllers/api/video-channel.ts29
-rw-r--r--server/controllers/api/videos/abuse.ts8
-rw-r--r--server/controllers/api/videos/comment.ts10
-rw-r--r--server/controllers/api/videos/index.ts14
-rw-r--r--server/helpers/audit-logger.ts138
-rw-r--r--server/lib/user.ts1
-rw-r--r--server/models/activitypub/actor.ts4
9 files changed, 249 insertions, 20 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index 9c1b2818c..411b13539 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -9,10 +9,13 @@ import { CONFIG, CONSTRAINTS_FIELDS, reloadConfig } from '../../initializers'
9import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' 9import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
10import { customConfigUpdateValidator } from '../../middlewares/validators/config' 10import { customConfigUpdateValidator } from '../../middlewares/validators/config'
11import { ClientHtml } from '../../lib/client-html' 11import { ClientHtml } from '../../lib/client-html'
12import { CustomConfigAuditView, auditLoggerFactory } from '../../helpers/audit-logger'
12 13
13const packageJSON = require('../../../../package.json') 14const packageJSON = require('../../../../package.json')
14const configRouter = express.Router() 15const configRouter = express.Router()
15 16
17const auditLogger = auditLoggerFactory('config')
18
16configRouter.get('/about', getAbout) 19configRouter.get('/about', getAbout)
17configRouter.get('/', 20configRouter.get('/',
18 asyncMiddleware(getConfig) 21 asyncMiddleware(getConfig)
@@ -119,6 +122,11 @@ async function getCustomConfig (req: express.Request, res: express.Response, nex
119async function deleteCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) { 122async function deleteCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
120 await unlinkPromise(CONFIG.CUSTOM_FILE) 123 await unlinkPromise(CONFIG.CUSTOM_FILE)
121 124
125 auditLogger.delete(
126 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
127 new CustomConfigAuditView(customConfig())
128 )
129
122 reloadConfig() 130 reloadConfig()
123 ClientHtml.invalidCache() 131 ClientHtml.invalidCache()
124 132
@@ -129,6 +137,7 @@ async function deleteCustomConfig (req: express.Request, res: express.Response,
129 137
130async function updateCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) { 138async function updateCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
131 const toUpdate: CustomConfig = req.body 139 const toUpdate: CustomConfig = req.body
140 const oldCustomConfigAuditKeys = new CustomConfigAuditView(customConfig())
132 141
133 // Force number conversion 142 // Force number conversion
134 toUpdate.cache.previews.size = parseInt('' + toUpdate.cache.previews.size, 10) 143 toUpdate.cache.previews.size = parseInt('' + toUpdate.cache.previews.size, 10)
@@ -150,6 +159,13 @@ async function updateCustomConfig (req: express.Request, res: express.Response,
150 ClientHtml.invalidCache() 159 ClientHtml.invalidCache()
151 160
152 const data = customConfig() 161 const data = customConfig()
162
163 auditLogger.update(
164 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
165 new CustomConfigAuditView(data),
166 oldCustomConfigAuditKeys
167 )
168
153 return res.json(data).end() 169 return res.json(data).end()
154} 170}
155 171
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index c80f27a23..dbe736bff 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -39,6 +39,9 @@ import { createReqFiles } from '../../helpers/express-utils'
39import { UserVideoQuota } from '../../../shared/models/users/user-video-quota.model' 39import { UserVideoQuota } from '../../../shared/models/users/user-video-quota.model'
40import { updateAvatarValidator } from '../../middlewares/validators/avatar' 40import { updateAvatarValidator } from '../../middlewares/validators/avatar'
41import { updateActorAvatarFile } from '../../lib/avatar' 41import { updateActorAvatarFile } from '../../lib/avatar'
42import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger'
43
44const auditLogger = auditLoggerFactory('users')
42 45
43const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) 46const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
44const loginRateLimiter = new RateLimit({ 47const loginRateLimiter = new RateLimit({
@@ -189,6 +192,7 @@ async function createUser (req: express.Request, res: express.Response) {
189 192
190 const { user, account } = await createUserAccountAndChannel(userToCreate) 193 const { user, account } = await createUserAccountAndChannel(userToCreate)
191 194
195 auditLogger.create(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON()))
192 logger.info('User %s with its channel and account created.', body.username) 196 logger.info('User %s with its channel and account created.', body.username)
193 197
194 return res.json({ 198 return res.json({
@@ -205,7 +209,7 @@ async function createUser (req: express.Request, res: express.Response) {
205async function registerUser (req: express.Request, res: express.Response) { 209async function registerUser (req: express.Request, res: express.Response) {
206 const body: UserCreate = req.body 210 const body: UserCreate = req.body
207 211
208 const user = new UserModel({ 212 const userToCreate = new UserModel({
209 username: body.username, 213 username: body.username,
210 password: body.password, 214 password: body.password,
211 email: body.email, 215 email: body.email,
@@ -215,8 +219,9 @@ async function registerUser (req: express.Request, res: express.Response) {
215 videoQuota: CONFIG.USER.VIDEO_QUOTA 219 videoQuota: CONFIG.USER.VIDEO_QUOTA
216 }) 220 })
217 221
218 await createUserAccountAndChannel(user) 222 const { user } = await createUserAccountAndChannel(userToCreate)
219 223
224 auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON()))
220 logger.info('User %s with its channel and account registered.', body.username) 225 logger.info('User %s with its channel and account registered.', body.username)
221 226
222 return res.type('json').status(204).end() 227 return res.type('json').status(204).end()
@@ -269,6 +274,8 @@ async function removeUser (req: express.Request, res: express.Response, next: ex
269 274
270 await user.destroy() 275 await user.destroy()
271 276
277 auditLogger.delete(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON()))
278
272 return res.sendStatus(204) 279 return res.sendStatus(204)
273} 280}
274 281
@@ -276,6 +283,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr
276 const body: UserUpdateMe = req.body 283 const body: UserUpdateMe = req.body
277 284
278 const user: UserModel = res.locals.oauth.token.user 285 const user: UserModel = res.locals.oauth.token.user
286 const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
279 287
280 if (body.password !== undefined) user.password = body.password 288 if (body.password !== undefined) user.password = body.password
281 if (body.email !== undefined) user.email = body.email 289 if (body.email !== undefined) user.email = body.email
@@ -290,6 +298,12 @@ async function updateMe (req: express.Request, res: express.Response, next: expr
290 await user.Account.save({ transaction: t }) 298 await user.Account.save({ transaction: t })
291 299
292 await sendUpdateActor(user.Account, t) 300 await sendUpdateActor(user.Account, t)
301
302 auditLogger.update(
303 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
304 new UserAuditView(user.toFormattedJSON()),
305 oldUserAuditView
306 )
293 }) 307 })
294 308
295 return res.sendStatus(204) 309 return res.sendStatus(204)
@@ -297,10 +311,18 @@ async function updateMe (req: express.Request, res: express.Response, next: expr
297 311
298async function updateMyAvatar (req: express.Request, res: express.Response, next: express.NextFunction) { 312async function updateMyAvatar (req: express.Request, res: express.Response, next: express.NextFunction) {
299 const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] 313 const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ]
300 const account = res.locals.oauth.token.user.Account 314 const user: UserModel = res.locals.oauth.token.user
315 const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
316 const account = user.Account
301 317
302 const avatar = await updateActorAvatarFile(avatarPhysicalFile, account.Actor, account) 318 const avatar = await updateActorAvatarFile(avatarPhysicalFile, account.Actor, account)
303 319
320 auditLogger.update(
321 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
322 new UserAuditView(user.toFormattedJSON()),
323 oldUserAuditView
324 )
325
304 return res 326 return res
305 .json({ 327 .json({
306 avatar: avatar.toFormattedJSON() 328 avatar: avatar.toFormattedJSON()
@@ -310,20 +332,27 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
310 332
311async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) { 333async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
312 const body: UserUpdate = req.body 334 const body: UserUpdate = req.body
313 const user = res.locals.user as UserModel 335 const userToUpdate = res.locals.user as UserModel
314 const roleChanged = body.role !== undefined && body.role !== user.role 336 const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON())
337 const roleChanged = body.role !== undefined && body.role !== userToUpdate.role
315 338
316 if (body.email !== undefined) user.email = body.email 339 if (body.email !== undefined) userToUpdate.email = body.email
317 if (body.videoQuota !== undefined) user.videoQuota = body.videoQuota 340 if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota
318 if (body.role !== undefined) user.role = body.role 341 if (body.role !== undefined) userToUpdate.role = body.role
319 342
320 await user.save() 343 const user = await userToUpdate.save()
321 344
322 // Destroy user token to refresh rights 345 // Destroy user token to refresh rights
323 if (roleChanged) { 346 if (roleChanged) {
324 await OAuthTokenModel.deleteUserToken(user.id) 347 await OAuthTokenModel.deleteUserToken(userToUpdate.id)
325 } 348 }
326 349
350 auditLogger.update(
351 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
352 new UserAuditView(user.toFormattedJSON()),
353 oldUserAuditView
354 )
355
327 // Don't need to send this update to followers, these attributes are not propagated 356 // Don't need to send this update to followers, these attributes are not propagated
328 357
329 return res.sendStatus(204) 358 return res.sendStatus(204)
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 0488ba8f5..3a444547b 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -27,7 +27,9 @@ import { logger } from '../../helpers/logger'
27import { VideoModel } from '../../models/video/video' 27import { VideoModel } from '../../models/video/video'
28import { updateAvatarValidator } from '../../middlewares/validators/avatar' 28import { updateAvatarValidator } from '../../middlewares/validators/avatar'
29import { updateActorAvatarFile } from '../../lib/avatar' 29import { updateActorAvatarFile } from '../../lib/avatar'
30import { auditLoggerFactory, VideoChannelAuditView } from '../../helpers/audit-logger'
30 31
32const auditLogger = auditLoggerFactory('channels')
31const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) 33const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
32 34
33const videoChannelRouter = express.Router() 35const videoChannelRouter = express.Router()
@@ -99,10 +101,17 @@ async function listVideoChannels (req: express.Request, res: express.Response, n
99 101
100async function updateVideoChannelAvatar (req: express.Request, res: express.Response, next: express.NextFunction) { 102async function updateVideoChannelAvatar (req: express.Request, res: express.Response, next: express.NextFunction) {
101 const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] 103 const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ]
102 const videoChannel = res.locals.videoChannel 104 const videoChannel = res.locals.videoChannel as VideoChannelModel
105 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
103 106
104 const avatar = await updateActorAvatarFile(avatarPhysicalFile, videoChannel.Actor, videoChannel) 107 const avatar = await updateActorAvatarFile(avatarPhysicalFile, videoChannel.Actor, videoChannel)
105 108
109 auditLogger.update(
110 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
111 new VideoChannelAuditView(videoChannel.toFormattedJSON()),
112 oldVideoChannelAuditKeys
113 )
114
106 return res 115 return res
107 .json({ 116 .json({
108 avatar: avatar.toFormattedJSON() 117 avatar: avatar.toFormattedJSON()
@@ -121,6 +130,10 @@ async function addVideoChannel (req: express.Request, res: express.Response) {
121 setAsyncActorKeys(videoChannelCreated.Actor) 130 setAsyncActorKeys(videoChannelCreated.Actor)
122 .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) 131 .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err }))
123 132
133 auditLogger.create(
134 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
135 new VideoChannelAuditView(videoChannelCreated.toFormattedJSON())
136 )
124 logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) 137 logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
125 138
126 return res.json({ 139 return res.json({
@@ -134,6 +147,7 @@ async function addVideoChannel (req: express.Request, res: express.Response) {
134async function updateVideoChannel (req: express.Request, res: express.Response) { 147async function updateVideoChannel (req: express.Request, res: express.Response) {
135 const videoChannelInstance = res.locals.videoChannel as VideoChannelModel 148 const videoChannelInstance = res.locals.videoChannel as VideoChannelModel
136 const videoChannelFieldsSave = videoChannelInstance.toJSON() 149 const videoChannelFieldsSave = videoChannelInstance.toJSON()
150 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
137 const videoChannelInfoToUpdate = req.body as VideoChannelUpdate 151 const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
138 152
139 try { 153 try {
@@ -148,9 +162,14 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
148 162
149 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) 163 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
150 await sendUpdateActor(videoChannelInstanceUpdated, t) 164 await sendUpdateActor(videoChannelInstanceUpdated, t)
151 })
152 165
153 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) 166 auditLogger.update(
167 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
168 new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
169 oldVideoChannelAuditKeys
170 )
171 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
172 })
154 } catch (err) { 173 } catch (err) {
155 logger.debug('Cannot update the video channel.', { err }) 174 logger.debug('Cannot update the video channel.', { err })
156 175
@@ -171,6 +190,10 @@ async function removeVideoChannel (req: express.Request, res: express.Response)
171 await sequelizeTypescript.transaction(async t => { 190 await sequelizeTypescript.transaction(async t => {
172 await videoChannelInstance.destroy({ transaction: t }) 191 await videoChannelInstance.destroy({ transaction: t })
173 192
193 auditLogger.delete(
194 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
195 new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
196 )
174 logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) 197 logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
175 }) 198 })
176 199
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index 3413ae894..7782fc639 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -18,7 +18,9 @@ import {
18import { AccountModel } from '../../../models/account/account' 18import { AccountModel } from '../../../models/account/account'
19import { VideoModel } from '../../../models/video/video' 19import { VideoModel } from '../../../models/video/video'
20import { VideoAbuseModel } from '../../../models/video/video-abuse' 20import { VideoAbuseModel } from '../../../models/video/video-abuse'
21import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger'
21 22
23const auditLogger = auditLoggerFactory('abuse')
22const abuseVideoRouter = express.Router() 24const abuseVideoRouter = express.Router()
23 25
24abuseVideoRouter.get('/abuse', 26abuseVideoRouter.get('/abuse',
@@ -64,14 +66,16 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
64 await sequelizeTypescript.transaction(async t => { 66 await sequelizeTypescript.transaction(async t => {
65 const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) 67 const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
66 videoAbuseInstance.Video = videoInstance 68 videoAbuseInstance.Video = videoInstance
69 videoAbuseInstance.Account = reporterAccount
67 70
68 // We send the video abuse to the origin server 71 // We send the video abuse to the origin server
69 if (videoInstance.isOwned() === false) { 72 if (videoInstance.isOwned() === false) {
70 await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance, t) 73 await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance, t)
71 } 74 }
72 })
73 75
74 logger.info('Abuse report for video %s created.', videoInstance.name) 76 auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON()))
77 logger.info('Abuse report for video %s created.', videoInstance.name)
78 })
75 79
76 return res.type('json').status(204).end() 80 return res.type('json').status(204).end()
77} 81}
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index bbeb0d557..e35247829 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -23,7 +23,9 @@ import {
23} from '../../../middlewares/validators/video-comments' 23} from '../../../middlewares/validators/video-comments'
24import { VideoModel } from '../../../models/video/video' 24import { VideoModel } from '../../../models/video/video'
25import { VideoCommentModel } from '../../../models/video/video-comment' 25import { VideoCommentModel } from '../../../models/video/video-comment'
26import { auditLoggerFactory, CommentAuditView } from '../../../helpers/audit-logger'
26 27
28const auditLogger = auditLoggerFactory('comments')
27const videoCommentRouter = express.Router() 29const videoCommentRouter = express.Router()
28 30
29videoCommentRouter.get('/:videoId/comment-threads', 31videoCommentRouter.get('/:videoId/comment-threads',
@@ -107,6 +109,8 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons
107 }, t) 109 }, t)
108 }) 110 })
109 111
112 auditLogger.create(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new CommentAuditView(comment.toFormattedJSON()))
113
110 return res.json({ 114 return res.json({
111 comment: comment.toFormattedJSON() 115 comment: comment.toFormattedJSON()
112 }).end() 116 }).end()
@@ -124,6 +128,8 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
124 }, t) 128 }, t)
125 }) 129 })
126 130
131 auditLogger.create(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new CommentAuditView(comment.toFormattedJSON()))
132
127 return res.json({ 133 return res.json({
128 comment: comment.toFormattedJSON() 134 comment: comment.toFormattedJSON()
129 }).end() 135 }).end()
@@ -136,6 +142,10 @@ async function removeVideoComment (req: express.Request, res: express.Response)
136 await videoCommentInstance.destroy({ transaction: t }) 142 await videoCommentInstance.destroy({ transaction: t })
137 }) 143 })
138 144
145 auditLogger.delete(
146 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
147 new CommentAuditView(videoCommentInstance.toFormattedJSON())
148 )
139 logger.info('Video comment %d deleted.', videoCommentInstance.id) 149 logger.info('Video comment %d deleted.', videoCommentInstance.id)
140 150
141 return res.type('json').status(204).end() 151 return res.type('json').status(204).end()
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 101183eab..e396ee6be 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -5,6 +5,7 @@ import { renamePromise } from '../../../helpers/core-utils'
5import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' 5import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
6import { processImage } from '../../../helpers/image-utils' 6import { processImage } from '../../../helpers/image-utils'
7import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
8import { auditLoggerFactory, VideoAuditView } from '../../../helpers/audit-logger'
8import { getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils' 9import { getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
9import { 10import {
10 CONFIG, 11 CONFIG,
@@ -54,6 +55,7 @@ import { createReqFiles, buildNSFWFilter } from '../../../helpers/express-utils'
54import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' 55import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
55import { videoCaptionsRouter } from './captions' 56import { videoCaptionsRouter } from './captions'
56 57
58const auditLogger = auditLoggerFactory('videos')
57const videosRouter = express.Router() 59const videosRouter = express.Router()
58 60
59const reqVideoFileAdd = createReqFiles( 61const reqVideoFileAdd = createReqFiles(
@@ -247,6 +249,7 @@ async function addVideo (req: express.Request, res: express.Response) {
247 249
248 await federateVideoIfNeeded(video, true, t) 250 await federateVideoIfNeeded(video, true, t)
249 251
252 auditLogger.create(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new VideoAuditView(videoCreated.toFormattedDetailsJSON()))
250 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) 253 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid)
251 254
252 return videoCreated 255 return videoCreated
@@ -273,6 +276,7 @@ async function addVideo (req: express.Request, res: express.Response) {
273async function updateVideo (req: express.Request, res: express.Response) { 276async function updateVideo (req: express.Request, res: express.Response) {
274 const videoInstance: VideoModel = res.locals.video 277 const videoInstance: VideoModel = res.locals.video
275 const videoFieldsSave = videoInstance.toJSON() 278 const videoFieldsSave = videoInstance.toJSON()
279 const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON())
276 const videoInfoToUpdate: VideoUpdate = req.body 280 const videoInfoToUpdate: VideoUpdate = req.body
277 const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE 281 const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
278 282
@@ -344,9 +348,14 @@ async function updateVideo (req: express.Request, res: express.Response) {
344 348
345 const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE 349 const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE
346 await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) 350 await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t)
347 })
348 351
349 logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid) 352 auditLogger.update(
353 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
354 new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()),
355 oldVideoAuditView
356 )
357 logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid)
358 })
350 } catch (err) { 359 } catch (err) {
351 // Force fields we want to update 360 // Force fields we want to update
352 // If the transaction is retried, sequelize will think the object has not changed 361 // If the transaction is retried, sequelize will think the object has not changed
@@ -423,6 +432,7 @@ async function removeVideo (req: express.Request, res: express.Response) {
423 await videoInstance.destroy({ transaction: t }) 432 await videoInstance.destroy({ transaction: t })
424 }) 433 })
425 434
435 auditLogger.delete(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new VideoAuditView(videoInstance.toFormattedDetailsJSON()))
426 logger.info('Video with name %s and uuid %s deleted.', videoInstance.name, videoInstance.uuid) 436 logger.info('Video with name %s and uuid %s deleted.', videoInstance.name, videoInstance.uuid)
427 437
428 return res.type('json').status(204).end() 438 return res.type('json').status(204).end()
diff --git a/server/helpers/audit-logger.ts b/server/helpers/audit-logger.ts
index 4b237316f..f6eea7d90 100644
--- a/server/helpers/audit-logger.ts
+++ b/server/helpers/audit-logger.ts
@@ -5,7 +5,9 @@ import * as flatten from 'flat'
5import * as winston from 'winston' 5import * as winston from 'winston'
6import { CONFIG } from '../initializers' 6import { CONFIG } from '../initializers'
7import { jsonLoggerFormat, labelFormatter } from './logger' 7import { jsonLoggerFormat, labelFormatter } from './logger'
8import { VideoDetails } from '../../shared' 8import { VideoDetails, User, VideoChannel, VideoAbuse } from '../../shared'
9import { VideoComment } from '../../shared/models/videos/video-comment.model'
10import { CustomConfig } from '../../shared/models/server/custom-config.model'
9 11
10enum AUDIT_TYPE { 12enum AUDIT_TYPE {
11 CREATE = 'create', 13 CREATE = 'create',
@@ -111,13 +113,143 @@ const videoKeysToKeep = [
111 'support', 113 'support',
112 'commentsEnabled' 114 'commentsEnabled'
113] 115]
114class VideoAuditView extends AuditEntity { 116class VideoAuditView extends EntityAuditView {
115 constructor (private video: VideoDetails) { 117 constructor (private video: VideoDetails) {
116 super(videoKeysToKeep, 'video', video) 118 super(videoKeysToKeep, 'video', video)
117 } 119 }
118} 120}
119 121
122const commentKeysToKeep = [
123 'id',
124 'text',
125 'threadId',
126 'inReplyToCommentId',
127 'videoId',
128 'createdAt',
129 'updatedAt',
130 'totalReplies',
131 'account-id',
132 'account-uuid',
133 'account-name'
134]
135class CommentAuditView extends EntityAuditView {
136 constructor (private comment: VideoComment) {
137 super(commentKeysToKeep, 'comment', comment)
138 }
139}
140
141const userKeysToKeep = [
142 'id',
143 'username',
144 'email',
145 'nsfwPolicy',
146 'autoPlayVideo',
147 'role',
148 'videoQuota',
149 'createdAt',
150 'account-id',
151 'account-uuid',
152 'account-name',
153 'account-followingCount',
154 'account-followersCount',
155 'account-createdAt',
156 'account-updatedAt',
157 'account-avatar-path',
158 'account-avatar-createdAt',
159 'account-avatar-updatedAt',
160 'account-displayName',
161 'account-description',
162 'videoChannels'
163]
164class UserAuditView extends EntityAuditView {
165 constructor (private user: User) {
166 super(userKeysToKeep, 'user', user)
167 }
168}
169
170const channelKeysToKeep = [
171 'id',
172 'uuid',
173 'name',
174 'followingCount',
175 'followersCount',
176 'createdAt',
177 'updatedAt',
178 'avatar-path',
179 'avatar-createdAt',
180 'avatar-updatedAt',
181 'displayName',
182 'description',
183 'support',
184 'isLocal',
185 'ownerAccount-id',
186 'ownerAccount-uuid',
187 'ownerAccount-name',
188 'ownerAccount-displayedName'
189]
190class VideoChannelAuditView extends EntityAuditView {
191 constructor (private channel: VideoChannel) {
192 super(channelKeysToKeep, 'channel', channel)
193 }
194}
195
196const videoAbuseKeysToKeep = [
197 'id',
198 'reason',
199 'reporterAccount',
200 'video-id',
201 'video-name',
202 'video-uuid',
203 'createdAt'
204]
205class VideoAbuseAuditView extends EntityAuditView {
206 constructor (private videoAbuse: VideoAbuse) {
207 super(videoAbuseKeysToKeep, 'abuse', videoAbuse)
208 }
209}
210
211const customConfigKeysToKeep = [
212 'instance-name',
213 'instance-shortDescription',
214 'instance-description',
215 'instance-terms',
216 'instance-defaultClientRoute',
217 'instance-defaultNSFWPolicy',
218 'instance-customizations-javascript',
219 'instance-customizations-css',
220 'services-twitter-username',
221 'services-twitter-whitelisted',
222 'cache-previews-size',
223 'cache-captions-size',
224 'signup-enabled',
225 'signup-limit',
226 'admin-email',
227 'user-videoQuota',
228 'transcoding-enabled',
229 'transcoding-threads',
230 'transcoding-resolutions'
231]
232class CustomConfigAuditView extends EntityAuditView {
233 constructor (customConfig: CustomConfig) {
234 const infos: any = customConfig
235 const resolutionsDict = infos.transcoding.resolutions
236 const resolutionsArray = []
237 Object.entries(resolutionsDict).forEach(([resolution, isEnabled]) => {
238 if (isEnabled) {
239 resolutionsArray.push(resolution)
240 }
241 })
242 infos.transcoding.resolutions = resolutionsArray
243 super(customConfigKeysToKeep, 'config', infos)
244 }
245}
246
120export { 247export {
121 auditLoggerFactory, 248 auditLoggerFactory,
122 VideoAuditView 249 VideoChannelAuditView,
250 CommentAuditView,
251 UserAuditView,
252 VideoAuditView,
253 VideoAbuseAuditView,
254 CustomConfigAuditView
123} 255}
diff --git a/server/lib/user.ts b/server/lib/user.ts
index ac5f55260..e7a45f5aa 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -17,6 +17,7 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse
17 17
18 const userCreated = await userToCreate.save(userOptions) 18 const userCreated = await userToCreate.save(userOptions)
19 const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t) 19 const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t)
20 userCreated.Account = accountCreated
20 21
21 const videoChannelDisplayName = `Default ${userCreated.username} channel` 22 const videoChannelDisplayName = `Default ${userCreated.username} channel`
22 const videoChannelInfo = { 23 const videoChannelInfo = {
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 267032e2a..aeb69e7b4 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -454,6 +454,10 @@ export class ActorModel extends Model<ActorModel> {
454 return 'acct:' + this.preferredUsername + '@' + this.getHost() 454 return 'acct:' + this.preferredUsername + '@' + this.getHost()
455 } 455 }
456 456
457 getIdentifier () {
458 return this.Server ? `${this.preferredUsername}@${this.Server.host}` : this.preferredUsername
459 }
460
457 getHost () { 461 getHost () {
458 return this.Server ? this.Server.host : CONFIG.WEBSERVER.HOST 462 return this.Server ? this.Server.host : CONFIG.WEBSERVER.HOST
459 } 463 }