Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Framasoft
PeerTube
PeerTube
Commits
ff9d43f6
Verified
Commit
ff9d43f6
authored
Jun 22, 2022
by
Chocobozzz
Browse files
Refactor video rights checker
parent
2e401e85
Changes
8
Hide whitespace changes
Inline
Side-by-side
server/middlewares/validators/feeds.ts
View file @
ff9d43f6
...
...
@@ -6,6 +6,7 @@ import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helper
import
{
logger
}
from
'
../../helpers/logger
'
import
{
areValidationErrors
,
checkCanSeeVideo
,
doesAccountIdExist
,
doesAccountNameWithHostExist
,
doesUserFeedTokenCorrespond
,
...
...
@@ -112,7 +113,10 @@ const videoCommentsFeedsValidator = [
return
res
.
fail
({
message
:
'
videoId cannot be mixed with a channel filter
'
})
}
if
(
req
.
query
.
videoId
&&
!
await
doesVideoExist
(
req
.
query
.
videoId
,
res
))
return
if
(
req
.
query
.
videoId
)
{
if
(
!
await
doesVideoExist
(
req
.
query
.
videoId
,
res
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
paramId
:
req
.
query
.
videoId
,
video
:
res
.
locals
.
videoAll
}))
return
}
return
next
()
}
...
...
server/middlewares/validators/shared/videos.ts
View file @
ff9d43f6
import
{
Request
,
Response
}
from
'
express
'
import
{
isUUIDValid
}
from
'
@server/helpers/custom-validators/misc
'
import
{
loadVideo
,
VideoLoadType
}
from
'
@server/lib/model-loaders
'
import
{
isAbleToUploadVideo
}
from
'
@server/lib/user
'
import
{
authenticatePromiseIfNeeded
}
from
'
@server/middlewares/auth
'
...
...
@@ -18,18 +19,19 @@ import {
MVideoThumbnail
,
MVideoWithRights
}
from
'
@server/types/models
'
import
{
HttpStatusCode
,
ServerErrorCode
,
UserRight
}
from
'
@shared/models
'
import
{
HttpStatusCode
,
ServerErrorCode
,
UserRight
,
VideoPrivacy
}
from
'
@shared/models
'
async
function
doesVideoExist
(
id
:
number
|
string
,
res
:
Response
,
fetchType
:
VideoLoadType
=
'
all
'
)
{
const
userId
=
res
.
locals
.
oauth
?
res
.
locals
.
oauth
.
token
.
User
.
id
:
undefined
const
video
=
await
loadVideo
(
id
,
fetchType
,
userId
)
if
(
video
===
null
)
{
if
(
!
video
)
{
res
.
fail
({
status
:
HttpStatusCode
.
NOT_FOUND_404
,
message
:
'
Video not found
'
})
return
false
}
...
...
@@ -58,6 +60,8 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi
return
true
}
// ---------------------------------------------------------------------------
async
function
doesVideoFileOfVideoExist
(
id
:
number
,
videoIdOrUUID
:
number
|
string
,
res
:
Response
)
{
if
(
!
await
VideoFileModel
.
doesVideoExistForVideoFile
(
id
,
videoIdOrUUID
))
{
res
.
fail
({
...
...
@@ -70,6 +74,8 @@ async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | st
return
true
}
// ---------------------------------------------------------------------------
async
function
doesVideoChannelOfAccountExist
(
channelId
:
number
,
user
:
MUserAccountId
,
res
:
Response
)
{
const
videoChannel
=
await
VideoChannelModel
.
loadAndPopulateAccount
(
channelId
)
...
...
@@ -95,32 +101,77 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAcc
return
true
}
async
function
checkCanSeeVideoIfPrivate
(
req
:
Request
,
res
:
Response
,
video
:
MVideo
,
authenticateInQuery
=
false
)
{
if
(
!
video
.
requiresAuth
())
return
true
// ---------------------------------------------------------------------------
const
videoWithRights
=
await
VideoModel
.
loadAndPopulateAccountAndServerAndTags
(
video
.
id
)
async
function
checkCanSeeVideo
(
options
:
{
req
:
Request
res
:
Response
paramId
:
string
video
:
MVideo
authenticateInQuery
?:
boolean
// default false
})
{
const
{
req
,
res
,
video
,
paramId
,
authenticateInQuery
=
false
}
=
options
if
(
video
.
requiresAuth
())
{
return
checkCanSeeAuthVideo
(
req
,
res
,
video
,
authenticateInQuery
)
}
return
checkCanSeePrivateVideo
(
req
,
res
,
videoWithRights
,
authenticateInQuery
)
}
if
(
video
.
privacy
===
VideoPrivacy
.
UNLISTED
)
{
if
(
isUUIDValid
(
paramId
))
return
true
async
functio
n
checkCanSee
Private
Video
(
req
:
Request
,
res
:
Response
,
video
:
MVideoWithRights
,
authenticateInQuery
=
false
)
{
await
authenticatePromiseIfNeeded
(
req
,
res
,
authenticateInQuery
)
retur
n
checkCanSee
Auth
Video
(
req
,
res
,
video
,
authenticateInQuery
)
}
const
user
=
res
.
locals
.
oauth
?
res
.
locals
.
oauth
.
token
.
User
:
null
if
(
video
.
privacy
===
VideoPrivacy
.
PUBLIC
)
return
true
throw
new
Error
(
'
Fatal error when checking video right
'
+
video
.
url
)
}
// Only the owner or a user that have blocklist rights can see the video
if
(
!
user
||
!
user
.
canGetVideo
(
video
))
{
async
function
checkCanSeeAuthVideo
(
req
:
Request
,
res
:
Response
,
video
:
MVideoId
|
MVideoWithRights
,
authenticateInQuery
=
false
)
{
const
fail
=
()
=>
{
res
.
fail
({
status
:
HttpStatusCode
.
FORBIDDEN_403
,
message
:
'
Cannot fetch information of private/internal/block
list
ed video
'
message
:
'
Cannot fetch information of private/internal/blocked video
'
})
return
false
}
return
true
await
authenticatePromiseIfNeeded
(
req
,
res
,
authenticateInQuery
)
const
user
=
res
.
locals
.
oauth
?.
token
.
User
if
(
!
user
)
return
fail
()
const
videoWithRights
=
(
video
as
MVideoWithRights
).
VideoChannel
?.
Account
?.
userId
?
video
as
MVideoWithRights
:
await
VideoModel
.
loadAndPopulateAccountAndServerAndTags
(
video
.
id
)
const
privacy
=
videoWithRights
.
privacy
if
(
privacy
===
VideoPrivacy
.
INTERNAL
)
{
// We know we have a user
return
true
}
const
isOwnedByUser
=
videoWithRights
.
VideoChannel
.
Account
.
userId
===
user
.
id
if
(
privacy
===
VideoPrivacy
.
PRIVATE
||
privacy
===
VideoPrivacy
.
UNLISTED
)
{
if
(
isOwnedByUser
&&
user
.
hasRight
(
UserRight
.
SEE_ALL_VIDEOS
))
return
true
return
fail
()
}
if
(
videoWithRights
.
isBlacklisted
())
{
if
(
isOwnedByUser
||
user
.
hasRight
(
UserRight
.
MANAGE_VIDEO_BLACKLIST
))
return
true
return
fail
()
}
// Should not happen
return
fail
()
}
// ---------------------------------------------------------------------------
function
checkUserCanManageVideo
(
user
:
MUser
,
video
:
MVideoAccountLight
,
right
:
UserRight
,
res
:
Response
,
onlyOwned
=
true
)
{
// Retrieve the user who did the request
if
(
onlyOwned
&&
video
.
isOwned
()
===
false
)
{
...
...
@@ -146,6 +197,8 @@ function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right:
return
true
}
// ---------------------------------------------------------------------------
async
function
checkUserQuota
(
user
:
MUserId
,
videoFileSize
:
number
,
res
:
Response
)
{
if
(
await
isAbleToUploadVideo
(
user
.
id
,
videoFileSize
)
===
false
)
{
res
.
fail
({
...
...
@@ -167,7 +220,6 @@ export {
doesVideoFileOfVideoExist
,
checkUserCanManageVideo
,
checkCanSeeVideoIfPrivate
,
checkCanSeePrivateVideo
,
checkCanSeeVideo
,
checkUserQuota
}
server/middlewares/validators/videos/video-captions.ts
View file @
ff9d43f6
...
...
@@ -7,7 +7,7 @@ import { logger } from '../../../helpers/logger'
import
{
CONSTRAINTS_FIELDS
,
MIMETYPES
}
from
'
../../../initializers/constants
'
import
{
areValidationErrors
,
checkCanSeeVideo
IfPrivate
,
checkCanSeeVideo
,
checkUserCanManageVideo
,
doesVideoCaptionExist
,
doesVideoExist
,
...
...
@@ -74,7 +74,7 @@ const listVideoCaptionsValidator = [
if
(
!
await
doesVideoExist
(
req
.
params
.
videoId
,
res
,
'
only-video
'
))
return
const
video
=
res
.
locals
.
onlyVideo
if
(
!
await
checkCanSeeVideo
IfPrivate
(
req
,
res
,
video
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
video
,
paramId
:
req
.
params
.
videoId
}
))
return
return
next
()
}
...
...
server/middlewares/validators/videos/video-comments.ts
View file @
ff9d43f6
...
...
@@ -10,7 +10,7 @@ import { Hooks } from '../../../lib/plugins/hooks'
import
{
MCommentOwnerVideoReply
,
MVideo
,
MVideoFullLight
}
from
'
../../../types/models/video
'
import
{
areValidationErrors
,
checkCanSeeVideo
IfPrivate
,
checkCanSeeVideo
,
doesVideoCommentExist
,
doesVideoCommentThreadExist
,
doesVideoExist
,
...
...
@@ -54,7 +54,7 @@ const listVideoCommentThreadsValidator = [
if
(
areValidationErrors
(
req
,
res
))
return
if
(
!
await
doesVideoExist
(
req
.
params
.
videoId
,
res
,
'
only-video
'
))
return
if
(
!
await
checkCanSeeVideo
IfPrivate
(
req
,
res
,
res
.
locals
.
onlyVideo
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
paramId
:
req
.
params
.
videoId
,
video
:
res
.
locals
.
onlyVideo
}
))
return
return
next
()
}
...
...
@@ -73,7 +73,7 @@ const listVideoThreadCommentsValidator = [
if
(
!
await
doesVideoExist
(
req
.
params
.
videoId
,
res
,
'
only-video
'
))
return
if
(
!
await
doesVideoCommentThreadExist
(
req
.
params
.
threadId
,
res
.
locals
.
onlyVideo
,
res
))
return
if
(
!
await
checkCanSeeVideo
IfPrivate
(
req
,
res
,
res
.
locals
.
onlyVideo
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
paramId
:
req
.
params
.
videoId
,
video
:
res
.
locals
.
onlyVideo
}
))
return
return
next
()
}
...
...
@@ -91,7 +91,7 @@ const addVideoCommentThreadValidator = [
if
(
areValidationErrors
(
req
,
res
))
return
if
(
!
await
doesVideoExist
(
req
.
params
.
videoId
,
res
))
return
if
(
!
await
checkCanSeeVideo
IfPrivate
(
req
,
res
,
res
.
locals
.
videoAll
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
paramId
:
req
.
params
.
videoId
,
video
:
res
.
locals
.
videoAll
}
))
return
if
(
!
isVideoCommentsEnabled
(
res
.
locals
.
videoAll
,
res
))
return
if
(
!
await
isVideoCommentAccepted
(
req
,
res
,
res
.
locals
.
videoAll
,
false
))
return
...
...
@@ -113,7 +113,7 @@ const addVideoCommentReplyValidator = [
if
(
areValidationErrors
(
req
,
res
))
return
if
(
!
await
doesVideoExist
(
req
.
params
.
videoId
,
res
))
return
if
(
!
await
checkCanSeeVideo
IfPrivate
(
req
,
res
,
res
.
locals
.
videoAll
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
paramId
:
req
.
params
.
videoId
,
video
:
res
.
locals
.
videoAll
}
))
return
if
(
!
isVideoCommentsEnabled
(
res
.
locals
.
videoAll
,
res
))
return
if
(
!
await
doesVideoCommentExist
(
req
.
params
.
commentId
,
res
.
locals
.
videoAll
,
res
))
return
...
...
server/middlewares/validators/videos/video-rates.ts
View file @
ff9d43f6
...
...
@@ -8,7 +8,7 @@ import { isRatingValid } from '../../../helpers/custom-validators/video-rates'
import
{
isVideoRatingTypeValid
}
from
'
../../../helpers/custom-validators/videos
'
import
{
logger
}
from
'
../../../helpers/logger
'
import
{
AccountVideoRateModel
}
from
'
../../../models/account/account-video-rate
'
import
{
areValidationErrors
,
checkCanSeeVideo
IfPrivate
,
doesVideoExist
,
isValidVideoIdParam
}
from
'
../shared
'
import
{
areValidationErrors
,
checkCanSeeVideo
,
doesVideoExist
,
isValidVideoIdParam
}
from
'
../shared
'
const
videoUpdateRateValidator
=
[
isValidVideoIdParam
(
'
id
'
),
...
...
@@ -21,7 +21,7 @@ const videoUpdateRateValidator = [
if
(
areValidationErrors
(
req
,
res
))
return
if
(
!
await
doesVideoExist
(
req
.
params
.
id
,
res
))
return
if
(
!
await
checkCanSeeVideo
IfPrivate
(
req
,
res
,
res
.
locals
.
videoAll
))
return
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
paramId
:
req
.
params
.
id
,
video
:
res
.
locals
.
videoAll
}
))
return
return
next
()
}
...
...
server/middlewares/validators/videos/videos.ts
View file @
ff9d43f6
...
...
@@ -7,14 +7,13 @@ import { getServerActor } from '@server/models/application/application'
import
{
ExpressPromiseHandler
}
from
'
@server/types/express-handler
'
import
{
MUserAccountId
,
MVideoFullLight
}
from
'
@server/types/models
'
import
{
getAllPrivacies
}
from
'
@shared/core-utils
'
import
{
HttpStatusCode
,
ServerErrorCode
,
UserRight
,
VideoInclude
,
VideoPrivacy
}
from
'
@shared/models
'
import
{
HttpStatusCode
,
ServerErrorCode
,
UserRight
,
VideoInclude
}
from
'
@shared/models
'
import
{
exists
,
isBooleanValid
,
isDateValid
,
isFileValid
,
isIdValid
,
isUUIDValid
,
toArray
,
toBooleanOrNull
,
toIntOrNull
,
...
...
@@ -50,7 +49,7 @@ import { Hooks } from '../../../lib/plugins/hooks'
import
{
VideoModel
}
from
'
../../../models/video/video
'
import
{
areValidationErrors
,
checkCanSee
Private
Video
,
checkCanSeeVideo
,
checkUserCanManageVideo
,
checkUserQuota
,
doesVideoChannelOfAccountExist
,
...
...
@@ -297,28 +296,9 @@ const videosCustomGetValidator = (
const
video
=
getVideoWithAttributes
(
res
)
as
MVideoFullLight
// Video private or blacklisted
if
(
video
.
requiresAuth
())
{
if
(
await
checkCanSeePrivateVideo
(
req
,
res
,
video
,
authenticateInQuery
))
{
return
next
()
}
if
(
!
await
checkCanSeeVideo
({
req
,
res
,
video
,
paramId
:
req
.
params
.
id
,
authenticateInQuery
}))
return
return
}
// Video is public, anyone can access it
if
(
video
.
privacy
===
VideoPrivacy
.
PUBLIC
)
return
next
()
// Video is unlisted, check we used the uuid to fetch it
if
(
video
.
privacy
===
VideoPrivacy
.
UNLISTED
)
{
if
(
isUUIDValid
(
req
.
params
.
id
))
return
next
()
// Don't leak this unlisted video
return
res
.
fail
({
status
:
HttpStatusCode
.
NOT_FOUND_404
,
message
:
'
Video not found
'
})
}
return
next
()
}
]
}
...
...
server/models/user/user.ts
View file @
ff9d43f6
...
...
@@ -29,12 +29,11 @@ import {
MUserDefault
,
MUserFormattable
,
MUserNotifSettingChannelDefault
,
MUserWithNotificationSetting
,
MVideoWithRights
MUserWithNotificationSetting
}
from
'
@server/types/models
'
import
{
AttributesOnly
}
from
'
@shared/typescript-utils
'
import
{
hasUserRight
,
USER_ROLE_LABELS
}
from
'
../../../shared/core-utils/users
'
import
{
AbuseState
,
MyUser
,
UserRight
,
VideoPlaylistType
,
VideoPrivacy
}
from
'
../../../shared/models
'
import
{
AbuseState
,
MyUser
,
UserRight
,
VideoPlaylistType
}
from
'
../../../shared/models
'
import
{
User
,
UserRole
}
from
'
../../../shared/models/users
'
import
{
UserAdminFlag
}
from
'
../../../shared/models/users/user-flag.model
'
import
{
NSFWPolicyType
}
from
'
../../../shared/models/videos/nsfw-policy.type
'
...
...
@@ -851,22 +850,6 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
.
then
(
u
=>
u
.
map
(
u
=>
u
.
username
))
}
canGetVideo
(
video
:
MVideoWithRights
)
{
const
videoUserId
=
video
.
VideoChannel
.
Account
.
userId
if
(
video
.
isBlacklisted
())
{
return
videoUserId
===
this
.
id
||
this
.
hasRight
(
UserRight
.
MANAGE_VIDEO_BLACKLIST
)
}
if
(
video
.
privacy
===
VideoPrivacy
.
PRIVATE
)
{
return
video
.
VideoChannel
&&
videoUserId
===
this
.
id
||
this
.
hasRight
(
UserRight
.
MANAGE_VIDEO_BLACKLIST
)
}
if
(
video
.
privacy
===
VideoPrivacy
.
INTERNAL
)
return
true
return
false
}
hasRight
(
right
:
UserRight
)
{
return
hasUserRight
(
this
.
role
,
right
)
}
...
...
server/tests/api/check-params/videos.ts
View file @
ff9d43f6
...
...
@@ -39,10 +39,7 @@ describe('Test videos API validator', function () {
await
setAccessTokensToServers
([
server
])
const
username
=
'
user1
'
const
password
=
'
my super password
'
await
server
.
users
.
create
({
username
:
username
,
password
:
password
})
userAccessToken
=
await
server
.
login
.
getAccessToken
({
username
,
password
})
userAccessToken
=
await
server
.
users
.
generateUserAndToken
(
'
user1
'
)
{
const
body
=
await
server
.
users
.
getMyInfo
()
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment