1
0
Fork 0

Avoids easy cheating on vidoe views

This commit is contained in:
Chocobozzz 2018-02-23 16:39:51 +01:00
parent e3bb78a213
commit b5c0e95544
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
5 changed files with 65 additions and 12 deletions

View File

@ -22,6 +22,7 @@ import {
import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub' import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub'
import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send' import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send'
import { JobQueue } from '../../../lib/job-queue' import { JobQueue } from '../../../lib/job-queue'
import { Redis } from '../../../lib/redis'
import { import {
asyncMiddleware, asyncMiddleware,
authenticate, authenticate,
@ -352,7 +353,16 @@ function getVideo (req: express.Request, res: express.Response) {
async function viewVideo (req: express.Request, res: express.Response) { async function viewVideo (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video const videoInstance = res.locals.video
const ip = req.ip
const exists = await Redis.Instance.isViewExists(ip, videoInstance.uuid)
if (exists) {
logger.debug('View for ip %s and video %s already exists.', ip, videoInstance.uuid)
return res.status(204).end()
}
await videoInstance.increment('views') await videoInstance.increment('views')
await Redis.Instance.setView(ip, videoInstance.uuid)
const serverAccount = await getServerActor() const serverAccount = await getServerActor()
if (videoInstance.isOwned()) { if (videoInstance.isOwned()) {

View File

@ -231,6 +231,8 @@ const CONSTRAINTS_FIELDS = {
} }
} }
let VIDEO_VIEW_LIFETIME = 60000 * 60 // 1 hour
const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = { const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = {
LIKE: 'like', LIKE: 'like',
DISLIKE: 'dislike' DISLIKE: 'dislike'
@ -400,6 +402,7 @@ if (isTestInstance() === true) {
ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL = 10 * 1000 // 10 seconds ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL = 10 * 1000 // 10 seconds
CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB
SCHEDULER_INTERVAL = 10000 SCHEDULER_INTERVAL = 10000
VIDEO_VIEW_LIFETIME = 1000 // 1 second
} }
updateWebserverConfig() updateWebserverConfig()
@ -442,7 +445,8 @@ export {
USER_PASSWORD_RESET_LIFETIME, USER_PASSWORD_RESET_LIFETIME,
IMAGE_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT,
SCHEDULER_INTERVAL, SCHEDULER_INTERVAL,
JOB_COMPLETED_LIFETIME JOB_COMPLETED_LIFETIME,
VIDEO_VIEW_LIFETIME
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -1,7 +1,7 @@
import { createClient, RedisClient } from 'redis' import { createClient, RedisClient } from 'redis'
import { logger } from '../helpers/logger' import { logger } from '../helpers/logger'
import { generateRandomString } from '../helpers/utils' import { generateRandomString } from '../helpers/utils'
import { CONFIG, USER_PASSWORD_RESET_LIFETIME } from '../initializers' import { CONFIG, USER_PASSWORD_RESET_LIFETIME, VIDEO_VIEW_LIFETIME } from '../initializers'
class Redis { class Redis {
@ -46,6 +46,14 @@ class Redis {
return this.getValue(this.generateResetPasswordKey(userId)) return this.getValue(this.generateResetPasswordKey(userId))
} }
setView (ip: string, videoUUID: string) {
return this.setValue(this.buildViewKey(ip, videoUUID), '1', VIDEO_VIEW_LIFETIME)
}
async isViewExists (ip: string, videoUUID: string) {
return this.exists(this.buildViewKey(ip, videoUUID))
}
private getValue (key: string) { private getValue (key: string) {
return new Promise<string>((res, rej) => { return new Promise<string>((res, rej) => {
this.client.get(this.prefix + key, (err, value) => { this.client.get(this.prefix + key, (err, value) => {
@ -68,10 +76,24 @@ class Redis {
}) })
} }
private exists (key: string) {
return new Promise<boolean>((res, rej) => {
this.client.exists(this.prefix + key, (err, existsNumber) => {
if (err) return rej(err)
return res(existsNumber === 1)
})
})
}
private generateResetPasswordKey (userId: number) { private generateResetPasswordKey (userId: number) {
return 'reset-password-' + userId return 'reset-password-' + userId
} }
private buildViewKey (ip: string, videoUUID: string) {
return videoUUID + '-' + ip
}
static get Instance () { static get Instance () {
return this.instance || (this.instance = new this()) return this.instance || (this.instance = new this())
} }

View File

@ -421,15 +421,22 @@ describe('Test multiple servers', function () {
}) })
it('Should view multiple videos on owned servers', async function () { it('Should view multiple videos on owned servers', async function () {
this.timeout(10000) this.timeout(15000)
const tasks: Promise<any>[] = [] const tasks: Promise<any>[] = []
tasks.push(viewVideo(servers[2].url, localVideosServer3[0])) await viewVideo(servers[2].url, localVideosServer3[0])
tasks.push(viewVideo(servers[2].url, localVideosServer3[0])) await viewVideo(servers[2].url, localVideosServer3[0])
tasks.push(viewVideo(servers[2].url, localVideosServer3[0])) await viewVideo(servers[2].url, localVideosServer3[0])
tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) await viewVideo(servers[2].url, localVideosServer3[1])
await Promise.all(tasks) await Promise.all(tasks)
await wait(1500)
await viewVideo(servers[2].url, localVideosServer3[0])
await wait(1500)
await viewVideo(servers[2].url, localVideosServer3[0])
await wait(5000) await wait(5000)

View File

@ -8,7 +8,7 @@ import {
checkVideoFilesWereRemoved, completeVideoCheck, flushTests, getVideo, getVideoCategories, getVideoLanguages, getVideoLicences, checkVideoFilesWereRemoved, completeVideoCheck, flushTests, getVideo, getVideoCategories, getVideoLanguages, getVideoLicences,
getVideoPrivacies, getVideosList, getVideosListPagination, getVideosListSort, killallServers, rateVideo, removeVideo, runServer, getVideoPrivacies, getVideosList, getVideosListPagination, getVideosListSort, killallServers, rateVideo, removeVideo, runServer,
searchVideo, searchVideoWithPagination, searchVideoWithSort, ServerInfo, setAccessTokensToServers, testImage, updateVideo, uploadVideo, searchVideo, searchVideoWithPagination, searchVideoWithSort, ServerInfo, setAccessTokensToServers, testImage, updateVideo, uploadVideo,
viewVideo viewVideo, wait
} from '../../utils' } from '../../utils'
const expect = chai.expect const expect = chai.expect
@ -149,8 +149,7 @@ describe('Test a single server', function () {
}) })
it('Should get and seed the uploaded video', async function () { it('Should get and seed the uploaded video', async function () {
// Yes, this could be long this.timeout(5000)
this.timeout(60000)
const res = await getVideosList(server.url) const res = await getVideosList(server.url)
@ -163,8 +162,7 @@ describe('Test a single server', function () {
}) })
it('Should get the video by UUID', async function () { it('Should get the video by UUID', async function () {
// Yes, this could be long this.timeout(5000)
this.timeout(60000)
const res = await getVideo(server.url, videoUUID) const res = await getVideo(server.url, videoUUID)
@ -173,10 +171,22 @@ describe('Test a single server', function () {
}) })
it('Should have the views updated', async function () { it('Should have the views updated', async function () {
this.timeout(10000)
await viewVideo(server.url, videoId) await viewVideo(server.url, videoId)
await viewVideo(server.url, videoId) await viewVideo(server.url, videoId)
await viewVideo(server.url, videoId) await viewVideo(server.url, videoId)
await wait(1500)
await viewVideo(server.url, videoId)
await viewVideo(server.url, videoId)
await wait(1500)
await viewVideo(server.url, videoId)
await viewVideo(server.url, videoId)
const res = await getVideo(server.url, videoId) const res = await getVideo(server.url, videoId)
const video = res.body const video = res.body