diff options
author | Chocobozzz <me@florianbigard.com> | 2022-06-22 09:44:08 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-06-22 10:25:31 +0200 |
commit | ff9d43f62a4f4737c5bfe955883b48c5440f323a (patch) | |
tree | 60593f4b57ec5cd712986a3db370f39b0b7a4cef /server/middlewares/validators/shared | |
parent | 2e401e8575decb1d491d0db48ca1ab1eba5b2a66 (diff) | |
download | PeerTube-ff9d43f62a4f4737c5bfe955883b48c5440f323a.tar.gz PeerTube-ff9d43f62a4f4737c5bfe955883b48c5440f323a.tar.zst PeerTube-ff9d43f62a4f4737c5bfe955883b48c5440f323a.zip |
Refactor video rights checker
Diffstat (limited to 'server/middlewares/validators/shared')
-rw-r--r-- | server/middlewares/validators/shared/videos.ts | 84 |
1 files changed, 68 insertions, 16 deletions
diff --git a/server/middlewares/validators/shared/videos.ts b/server/middlewares/validators/shared/videos.ts index 8807435f6..39aab6df7 100644 --- a/server/middlewares/validators/shared/videos.ts +++ b/server/middlewares/validators/shared/videos.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Request, Response } from 'express' | 1 | import { Request, Response } from 'express' |
2 | import { isUUIDValid } from '@server/helpers/custom-validators/misc' | ||
2 | import { loadVideo, VideoLoadType } from '@server/lib/model-loaders' | 3 | import { loadVideo, VideoLoadType } from '@server/lib/model-loaders' |
3 | import { isAbleToUploadVideo } from '@server/lib/user' | 4 | import { isAbleToUploadVideo } from '@server/lib/user' |
4 | import { authenticatePromiseIfNeeded } from '@server/middlewares/auth' | 5 | import { authenticatePromiseIfNeeded } from '@server/middlewares/auth' |
@@ -18,18 +19,19 @@ import { | |||
18 | MVideoThumbnail, | 19 | MVideoThumbnail, |
19 | MVideoWithRights | 20 | MVideoWithRights |
20 | } from '@server/types/models' | 21 | } from '@server/types/models' |
21 | import { HttpStatusCode, ServerErrorCode, UserRight } from '@shared/models' | 22 | import { HttpStatusCode, ServerErrorCode, UserRight, VideoPrivacy } from '@shared/models' |
22 | 23 | ||
23 | async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') { | 24 | async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') { |
24 | const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined | 25 | const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined |
25 | 26 | ||
26 | const video = await loadVideo(id, fetchType, userId) | 27 | const video = await loadVideo(id, fetchType, userId) |
27 | 28 | ||
28 | if (video === null) { | 29 | if (!video) { |
29 | res.fail({ | 30 | res.fail({ |
30 | status: HttpStatusCode.NOT_FOUND_404, | 31 | status: HttpStatusCode.NOT_FOUND_404, |
31 | message: 'Video not found' | 32 | message: 'Video not found' |
32 | }) | 33 | }) |
34 | |||
33 | return false | 35 | return false |
34 | } | 36 | } |
35 | 37 | ||
@@ -58,6 +60,8 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi | |||
58 | return true | 60 | return true |
59 | } | 61 | } |
60 | 62 | ||
63 | // --------------------------------------------------------------------------- | ||
64 | |||
61 | async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | string, res: Response) { | 65 | async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | string, res: Response) { |
62 | if (!await VideoFileModel.doesVideoExistForVideoFile(id, videoIdOrUUID)) { | 66 | if (!await VideoFileModel.doesVideoExistForVideoFile(id, videoIdOrUUID)) { |
63 | res.fail({ | 67 | res.fail({ |
@@ -70,6 +74,8 @@ async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | st | |||
70 | return true | 74 | return true |
71 | } | 75 | } |
72 | 76 | ||
77 | // --------------------------------------------------------------------------- | ||
78 | |||
73 | async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) { | 79 | async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) { |
74 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) | 80 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) |
75 | 81 | ||
@@ -95,32 +101,77 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAcc | |||
95 | return true | 101 | return true |
96 | } | 102 | } |
97 | 103 | ||
98 | async function checkCanSeeVideoIfPrivate (req: Request, res: Response, video: MVideo, authenticateInQuery = false) { | 104 | // --------------------------------------------------------------------------- |
99 | if (!video.requiresAuth()) return true | ||
100 | 105 | ||
101 | const videoWithRights = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id) | 106 | async function checkCanSeeVideo (options: { |
107 | req: Request | ||
108 | res: Response | ||
109 | paramId: string | ||
110 | video: MVideo | ||
111 | authenticateInQuery?: boolean // default false | ||
112 | }) { | ||
113 | const { req, res, video, paramId, authenticateInQuery = false } = options | ||
114 | |||
115 | if (video.requiresAuth()) { | ||
116 | return checkCanSeeAuthVideo(req, res, video, authenticateInQuery) | ||
117 | } | ||
102 | 118 | ||
103 | return checkCanSeePrivateVideo(req, res, videoWithRights, authenticateInQuery) | 119 | if (video.privacy === VideoPrivacy.UNLISTED) { |
104 | } | 120 | if (isUUIDValid(paramId)) return true |
105 | 121 | ||
106 | async function checkCanSeePrivateVideo (req: Request, res: Response, video: MVideoWithRights, authenticateInQuery = false) { | 122 | return checkCanSeeAuthVideo(req, res, video, authenticateInQuery) |
107 | await authenticatePromiseIfNeeded(req, res, authenticateInQuery) | 123 | } |
108 | 124 | ||
109 | const user = res.locals.oauth ? res.locals.oauth.token.User : null | 125 | if (video.privacy === VideoPrivacy.PUBLIC) return true |
126 | |||
127 | throw new Error('Fatal error when checking video right ' + video.url) | ||
128 | } | ||
110 | 129 | ||
111 | // Only the owner or a user that have blocklist rights can see the video | 130 | async function checkCanSeeAuthVideo (req: Request, res: Response, video: MVideoId | MVideoWithRights, authenticateInQuery = false) { |
112 | if (!user || !user.canGetVideo(video)) { | 131 | const fail = () => { |
113 | res.fail({ | 132 | res.fail({ |
114 | status: HttpStatusCode.FORBIDDEN_403, | 133 | status: HttpStatusCode.FORBIDDEN_403, |
115 | message: 'Cannot fetch information of private/internal/blocklisted video' | 134 | message: 'Cannot fetch information of private/internal/blocked video' |
116 | }) | 135 | }) |
117 | 136 | ||
118 | return false | 137 | return false |
119 | } | 138 | } |
120 | 139 | ||
121 | return true | 140 | await authenticatePromiseIfNeeded(req, res, authenticateInQuery) |
141 | |||
142 | const user = res.locals.oauth?.token.User | ||
143 | if (!user) return fail() | ||
144 | |||
145 | const videoWithRights = (video as MVideoWithRights).VideoChannel?.Account?.userId | ||
146 | ? video as MVideoWithRights | ||
147 | : await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id) | ||
148 | |||
149 | const privacy = videoWithRights.privacy | ||
150 | |||
151 | if (privacy === VideoPrivacy.INTERNAL) { | ||
152 | // We know we have a user | ||
153 | return true | ||
154 | } | ||
155 | |||
156 | const isOwnedByUser = videoWithRights.VideoChannel.Account.userId === user.id | ||
157 | if (privacy === VideoPrivacy.PRIVATE || privacy === VideoPrivacy.UNLISTED) { | ||
158 | if (isOwnedByUser && user.hasRight(UserRight.SEE_ALL_VIDEOS)) return true | ||
159 | |||
160 | return fail() | ||
161 | } | ||
162 | |||
163 | if (videoWithRights.isBlacklisted()) { | ||
164 | if (isOwnedByUser || user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) return true | ||
165 | |||
166 | return fail() | ||
167 | } | ||
168 | |||
169 | // Should not happen | ||
170 | return fail() | ||
122 | } | 171 | } |
123 | 172 | ||
173 | // --------------------------------------------------------------------------- | ||
174 | |||
124 | function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response, onlyOwned = true) { | 175 | function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response, onlyOwned = true) { |
125 | // Retrieve the user who did the request | 176 | // Retrieve the user who did the request |
126 | if (onlyOwned && video.isOwned() === false) { | 177 | if (onlyOwned && video.isOwned() === false) { |
@@ -146,6 +197,8 @@ function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: | |||
146 | return true | 197 | return true |
147 | } | 198 | } |
148 | 199 | ||
200 | // --------------------------------------------------------------------------- | ||
201 | |||
149 | async function checkUserQuota (user: MUserId, videoFileSize: number, res: Response) { | 202 | async function checkUserQuota (user: MUserId, videoFileSize: number, res: Response) { |
150 | if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { | 203 | if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { |
151 | res.fail({ | 204 | res.fail({ |
@@ -167,7 +220,6 @@ export { | |||
167 | doesVideoFileOfVideoExist, | 220 | doesVideoFileOfVideoExist, |
168 | 221 | ||
169 | checkUserCanManageVideo, | 222 | checkUserCanManageVideo, |
170 | checkCanSeeVideoIfPrivate, | 223 | checkCanSeeVideo, |
171 | checkCanSeePrivateVideo, | ||
172 | checkUserQuota | 224 | checkUserQuota |
173 | } | 225 | } |