aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/server-commands
diff options
context:
space:
mode:
authorWicklow <123956049+wickloww@users.noreply.github.com>2023-06-29 07:48:55 +0000
committerGitHub <noreply@github.com>2023-06-29 09:48:55 +0200
commit40346ead2b0b7afa475aef057d3673b6c7574b7a (patch)
tree24ffdc23c3a9d987334842e0d400b5bd44500cf7 /shared/server-commands
parentae22c59f14d0d553f60b281948b6c232c2aca178 (diff)
downloadPeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.gz
PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.zst
PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.zip
Feature/password protected videos (#5836)
* Add server endpoints * Refactoring test suites * Update server and add openapi documentation * fix compliation and tests * upload/import password protected video on client * add server error code * Add video password to update resolver * add custom message when sharing pw protected video * improve confirm component * Add new alert in component * Add ability to watch protected video on client * Cannot have password protected replay privacy * Add migration * Add tests * update after review * Update check params tests * Add live videos test * Add more filter test * Update static file privacy test * Update object storage tests * Add test on feeds * Add missing word * Fix tests * Fix tests on live videos * add embed support on password protected videos * fix style * Correcting data leaks * Unable to add password protected privacy on replay * Updated code based on review comments * fix validator and command * Updated code based on review comments
Diffstat (limited to 'shared/server-commands')
-rw-r--r--shared/server-commands/requests/requests.ts4
-rw-r--r--shared/server-commands/server/server.ts3
-rw-r--r--shared/server-commands/shared/abstract-command.ts18
-rw-r--r--shared/server-commands/videos/captions-command.ts4
-rw-r--r--shared/server-commands/videos/comments-command.ts12
-rw-r--r--shared/server-commands/videos/index.ts1
-rw-r--r--shared/server-commands/videos/live-command.ts12
-rw-r--r--shared/server-commands/videos/video-passwords-command.ts55
-rw-r--r--shared/server-commands/videos/video-token-command.ts5
-rw-r--r--shared/server-commands/videos/videos-command.ts23
10 files changed, 123 insertions, 14 deletions
diff --git a/shared/server-commands/requests/requests.ts b/shared/server-commands/requests/requests.ts
index e3f1817f1..8227017eb 100644
--- a/shared/server-commands/requests/requests.ts
+++ b/shared/server-commands/requests/requests.ts
@@ -29,6 +29,7 @@ function makeRawRequest (options: {
29 range?: string 29 range?: string
30 query?: { [ id: string ]: string } 30 query?: { [ id: string ]: string }
31 method?: 'GET' | 'POST' 31 method?: 'GET' | 'POST'
32 headers?: { [ name: string ]: string }
32}) { 33}) {
33 const { host, protocol, pathname } = new URL(options.url) 34 const { host, protocol, pathname } = new URL(options.url)
34 35
@@ -37,7 +38,7 @@ function makeRawRequest (options: {
37 path: pathname, 38 path: pathname,
38 contentType: undefined, 39 contentType: undefined,
39 40
40 ...pick(options, [ 'expectedStatus', 'range', 'token', 'query' ]) 41 ...pick(options, [ 'expectedStatus', 'range', 'token', 'query', 'headers' ])
41 } 42 }
42 43
43 if (options.method === 'POST') { 44 if (options.method === 'POST') {
@@ -132,6 +133,7 @@ function makePutBodyRequest (options: {
132 token?: string 133 token?: string
133 fields: { [ fieldName: string ]: any } 134 fields: { [ fieldName: string ]: any }
134 expectedStatus?: HttpStatusCode 135 expectedStatus?: HttpStatusCode
136 headers?: { [name: string]: string }
135}) { 137}) {
136 const req = request(options.url).put(options.path) 138 const req = request(options.url).put(options.path)
137 .send(options.fields) 139 .send(options.fields)
diff --git a/shared/server-commands/server/server.ts b/shared/server-commands/server/server.ts
index 70f7a3ee2..0911e22b0 100644
--- a/shared/server-commands/server/server.ts
+++ b/shared/server-commands/server/server.ts
@@ -32,6 +32,7 @@ import {
32 HistoryCommand, 32 HistoryCommand,
33 ImportsCommand, 33 ImportsCommand,
34 LiveCommand, 34 LiveCommand,
35 VideoPasswordsCommand,
35 PlaylistsCommand, 36 PlaylistsCommand,
36 ServicesCommand, 37 ServicesCommand,
37 StreamingPlaylistsCommand, 38 StreamingPlaylistsCommand,
@@ -146,6 +147,7 @@ export class PeerTubeServer {
146 twoFactor?: TwoFactorCommand 147 twoFactor?: TwoFactorCommand
147 videoToken?: VideoTokenCommand 148 videoToken?: VideoTokenCommand
148 registrations?: RegistrationsCommand 149 registrations?: RegistrationsCommand
150 videoPasswords?: VideoPasswordsCommand
149 151
150 runners?: RunnersCommand 152 runners?: RunnersCommand
151 runnerRegistrationTokens?: RunnerRegistrationTokensCommand 153 runnerRegistrationTokens?: RunnerRegistrationTokensCommand
@@ -437,5 +439,6 @@ export class PeerTubeServer {
437 this.runners = new RunnersCommand(this) 439 this.runners = new RunnersCommand(this)
438 this.runnerRegistrationTokens = new RunnerRegistrationTokensCommand(this) 440 this.runnerRegistrationTokens = new RunnerRegistrationTokensCommand(this)
439 this.runnerJobs = new RunnerJobsCommand(this) 441 this.runnerJobs = new RunnerJobsCommand(this)
442 this.videoPasswords = new VideoPasswordsCommand(this)
440 } 443 }
441} 444}
diff --git a/shared/server-commands/shared/abstract-command.ts b/shared/server-commands/shared/abstract-command.ts
index ca4ffada9..463acc26b 100644
--- a/shared/server-commands/shared/abstract-command.ts
+++ b/shared/server-commands/shared/abstract-command.ts
@@ -101,25 +101,29 @@ abstract class AbstractCommand {
101 101
102 protected putBodyRequest (options: InternalCommonCommandOptions & { 102 protected putBodyRequest (options: InternalCommonCommandOptions & {
103 fields?: { [ fieldName: string ]: any } 103 fields?: { [ fieldName: string ]: any }
104 headers?: { [name: string]: string }
104 }) { 105 }) {
105 const { fields } = options 106 const { fields, headers } = options
106 107
107 return makePutBodyRequest({ 108 return makePutBodyRequest({
108 ...this.buildCommonRequestOptions(options), 109 ...this.buildCommonRequestOptions(options),
109 110
110 fields 111 fields,
112 headers
111 }) 113 })
112 } 114 }
113 115
114 protected postBodyRequest (options: InternalCommonCommandOptions & { 116 protected postBodyRequest (options: InternalCommonCommandOptions & {
115 fields?: { [ fieldName: string ]: any } 117 fields?: { [ fieldName: string ]: any }
118 headers?: { [name: string]: string }
116 }) { 119 }) {
117 const { fields } = options 120 const { fields, headers } = options
118 121
119 return makePostBodyRequest({ 122 return makePostBodyRequest({
120 ...this.buildCommonRequestOptions(options), 123 ...this.buildCommonRequestOptions(options),
121 124
122 fields 125 fields,
126 headers
123 }) 127 })
124 } 128 }
125 129
@@ -206,6 +210,12 @@ abstract class AbstractCommand {
206 210
207 return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus 211 return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus
208 } 212 }
213
214 protected buildVideoPasswordHeader (videoPassword: string) {
215 return videoPassword !== undefined && videoPassword !== null
216 ? { 'x-peertube-video-password': videoPassword }
217 : undefined
218 }
209} 219}
210 220
211export { 221export {
diff --git a/shared/server-commands/videos/captions-command.ts b/shared/server-commands/videos/captions-command.ts
index 62bf9c5e6..a26fcb57d 100644
--- a/shared/server-commands/videos/captions-command.ts
+++ b/shared/server-commands/videos/captions-command.ts
@@ -34,14 +34,16 @@ export class CaptionsCommand extends AbstractCommand {
34 34
35 list (options: OverrideCommandOptions & { 35 list (options: OverrideCommandOptions & {
36 videoId: string | number 36 videoId: string | number
37 videoPassword?: string
37 }) { 38 }) {
38 const { videoId } = options 39 const { videoId, videoPassword } = options
39 const path = '/api/v1/videos/' + videoId + '/captions' 40 const path = '/api/v1/videos/' + videoId + '/captions'
40 41
41 return this.getRequestBody<ResultList<VideoCaption>>({ 42 return this.getRequestBody<ResultList<VideoCaption>>({
42 ...options, 43 ...options,
43 44
44 path, 45 path,
46 headers: this.buildVideoPasswordHeader(videoPassword),
45 implicitToken: false, 47 implicitToken: false,
46 defaultExpectedStatus: HttpStatusCode.OK_200 48 defaultExpectedStatus: HttpStatusCode.OK_200
47 }) 49 })
diff --git a/shared/server-commands/videos/comments-command.ts b/shared/server-commands/videos/comments-command.ts
index 154ec0c24..0dab1b66a 100644
--- a/shared/server-commands/videos/comments-command.ts
+++ b/shared/server-commands/videos/comments-command.ts
@@ -36,11 +36,12 @@ export class CommentsCommand extends AbstractCommand {
36 36
37 listThreads (options: OverrideCommandOptions & { 37 listThreads (options: OverrideCommandOptions & {
38 videoId: number | string 38 videoId: number | string
39 videoPassword?: string
39 start?: number 40 start?: number
40 count?: number 41 count?: number
41 sort?: string 42 sort?: string
42 }) { 43 }) {
43 const { start, count, sort, videoId } = options 44 const { start, count, sort, videoId, videoPassword } = options
44 const path = '/api/v1/videos/' + videoId + '/comment-threads' 45 const path = '/api/v1/videos/' + videoId + '/comment-threads'
45 46
46 return this.getRequestBody<VideoCommentThreads>({ 47 return this.getRequestBody<VideoCommentThreads>({
@@ -48,6 +49,7 @@ export class CommentsCommand extends AbstractCommand {
48 49
49 path, 50 path,
50 query: { start, count, sort }, 51 query: { start, count, sort },
52 headers: this.buildVideoPasswordHeader(videoPassword),
51 implicitToken: false, 53 implicitToken: false,
52 defaultExpectedStatus: HttpStatusCode.OK_200 54 defaultExpectedStatus: HttpStatusCode.OK_200
53 }) 55 })
@@ -72,8 +74,9 @@ export class CommentsCommand extends AbstractCommand {
72 async createThread (options: OverrideCommandOptions & { 74 async createThread (options: OverrideCommandOptions & {
73 videoId: number | string 75 videoId: number | string
74 text: string 76 text: string
77 videoPassword?: string
75 }) { 78 }) {
76 const { videoId, text } = options 79 const { videoId, text, videoPassword } = options
77 const path = '/api/v1/videos/' + videoId + '/comment-threads' 80 const path = '/api/v1/videos/' + videoId + '/comment-threads'
78 81
79 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ 82 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({
@@ -81,6 +84,7 @@ export class CommentsCommand extends AbstractCommand {
81 84
82 path, 85 path,
83 fields: { text }, 86 fields: { text },
87 headers: this.buildVideoPasswordHeader(videoPassword),
84 implicitToken: true, 88 implicitToken: true,
85 defaultExpectedStatus: HttpStatusCode.OK_200 89 defaultExpectedStatus: HttpStatusCode.OK_200
86 })) 90 }))
@@ -95,8 +99,9 @@ export class CommentsCommand extends AbstractCommand {
95 videoId: number | string 99 videoId: number | string
96 toCommentId: number 100 toCommentId: number
97 text: string 101 text: string
102 videoPassword?: string
98 }) { 103 }) {
99 const { videoId, toCommentId, text } = options 104 const { videoId, toCommentId, text, videoPassword } = options
100 const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId 105 const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId
101 106
102 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ 107 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({
@@ -104,6 +109,7 @@ export class CommentsCommand extends AbstractCommand {
104 109
105 path, 110 path,
106 fields: { text }, 111 fields: { text },
112 headers: this.buildVideoPasswordHeader(videoPassword),
107 implicitToken: true, 113 implicitToken: true,
108 defaultExpectedStatus: HttpStatusCode.OK_200 114 defaultExpectedStatus: HttpStatusCode.OK_200
109 })) 115 }))
diff --git a/shared/server-commands/videos/index.ts b/shared/server-commands/videos/index.ts
index c17f6ef20..da36b5b6b 100644
--- a/shared/server-commands/videos/index.ts
+++ b/shared/server-commands/videos/index.ts
@@ -17,3 +17,4 @@ export * from './video-studio-command'
17export * from './video-token-command' 17export * from './video-token-command'
18export * from './views-command' 18export * from './views-command'
19export * from './videos-command' 19export * from './videos-command'
20export * from './video-passwords-command'
diff --git a/shared/server-commands/videos/live-command.ts b/shared/server-commands/videos/live-command.ts
index 44d625970..6006d9fe9 100644
--- a/shared/server-commands/videos/live-command.ts
+++ b/shared/server-commands/videos/live-command.ts
@@ -120,8 +120,13 @@ export class LiveCommand extends AbstractCommand {
120 saveReplay: boolean 120 saveReplay: boolean
121 permanentLive: boolean 121 permanentLive: boolean
122 privacy?: VideoPrivacy 122 privacy?: VideoPrivacy
123 videoPasswords?: string[]
123 }) { 124 }) {
124 const { saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC } = options 125 const { saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC, videoPasswords } = options
126
127 const replaySettings = privacy === VideoPrivacy.PASSWORD_PROTECTED
128 ? { privacy: VideoPrivacy.PRIVATE }
129 : { privacy }
125 130
126 const { uuid } = await this.create({ 131 const { uuid } = await this.create({
127 ...options, 132 ...options,
@@ -130,9 +135,10 @@ export class LiveCommand extends AbstractCommand {
130 name: 'live', 135 name: 'live',
131 permanentLive, 136 permanentLive,
132 saveReplay, 137 saveReplay,
133 replaySettings: { privacy }, 138 replaySettings,
134 channelId: this.server.store.channel.id, 139 channelId: this.server.store.channel.id,
135 privacy 140 privacy,
141 videoPasswords
136 } 142 }
137 }) 143 })
138 144
diff --git a/shared/server-commands/videos/video-passwords-command.ts b/shared/server-commands/videos/video-passwords-command.ts
new file mode 100644
index 000000000..bf10335b4
--- /dev/null
+++ b/shared/server-commands/videos/video-passwords-command.ts
@@ -0,0 +1,55 @@
1import { HttpStatusCode, ResultList, VideoPassword } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3export class VideoPasswordsCommand extends AbstractCommand {
4
5 list (options: OverrideCommandOptions & {
6 videoId: number | string
7 start?: number
8 count?: number
9 sort?: string
10 }) {
11 const { start, count, sort, videoId } = options
12 const path = '/api/v1/videos/' + videoId + '/passwords'
13
14 return this.getRequestBody<ResultList<VideoPassword>>({
15 ...options,
16
17 path,
18 query: { start, count, sort },
19 implicitToken: true,
20 defaultExpectedStatus: HttpStatusCode.OK_200
21 })
22 }
23
24 updateAll (options: OverrideCommandOptions & {
25 videoId: number | string
26 passwords: string[]
27 }) {
28 const { videoId, passwords } = options
29 const path = `/api/v1/videos/${videoId}/passwords`
30
31 return this.putBodyRequest({
32 ...options,
33 path,
34 fields: { passwords },
35 implicitToken: true,
36 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
37 })
38 }
39
40 remove (options: OverrideCommandOptions & {
41 id: number
42 videoId: number | string
43 }) {
44 const { id, videoId } = options
45 const path = `/api/v1/videos/${videoId}/passwords/${id}`
46
47 return this.deleteRequest({
48 ...options,
49
50 path,
51 implicitToken: true,
52 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
53 })
54 }
55}
diff --git a/shared/server-commands/videos/video-token-command.ts b/shared/server-commands/videos/video-token-command.ts
index 0531bee65..c4ed29a8c 100644
--- a/shared/server-commands/videos/video-token-command.ts
+++ b/shared/server-commands/videos/video-token-command.ts
@@ -8,12 +8,14 @@ export class VideoTokenCommand extends AbstractCommand {
8 8
9 create (options: OverrideCommandOptions & { 9 create (options: OverrideCommandOptions & {
10 videoId: number | string 10 videoId: number | string
11 videoPassword?: string
11 }) { 12 }) {
12 const { videoId } = options 13 const { videoId, videoPassword } = options
13 const path = '/api/v1/videos/' + videoId + '/token' 14 const path = '/api/v1/videos/' + videoId + '/token'
14 15
15 return unwrapBody<VideoToken>(this.postBodyRequest({ 16 return unwrapBody<VideoToken>(this.postBodyRequest({
16 ...options, 17 ...options,
18 headers: this.buildVideoPasswordHeader(videoPassword),
17 19
18 path, 20 path,
19 implicitToken: true, 21 implicitToken: true,
@@ -23,6 +25,7 @@ export class VideoTokenCommand extends AbstractCommand {
23 25
24 async getVideoFileToken (options: OverrideCommandOptions & { 26 async getVideoFileToken (options: OverrideCommandOptions & {
25 videoId: number | string 27 videoId: number | string
28 videoPassword?: string
26 }) { 29 }) {
27 const { files } = await this.create(options) 30 const { files } = await this.create(options)
28 31
diff --git a/shared/server-commands/videos/videos-command.ts b/shared/server-commands/videos/videos-command.ts
index b5df9c325..93ca623e1 100644
--- a/shared/server-commands/videos/videos-command.ts
+++ b/shared/server-commands/videos/videos-command.ts
@@ -111,8 +111,9 @@ export class VideosCommand extends AbstractCommand {
111 rate (options: OverrideCommandOptions & { 111 rate (options: OverrideCommandOptions & {
112 id: number | string 112 id: number | string
113 rating: UserVideoRateType 113 rating: UserVideoRateType
114 videoPassword?: string
114 }) { 115 }) {
115 const { id, rating } = options 116 const { id, rating, videoPassword } = options
116 const path = '/api/v1/videos/' + id + '/rate' 117 const path = '/api/v1/videos/' + id + '/rate'
117 118
118 return this.putBodyRequest({ 119 return this.putBodyRequest({
@@ -120,6 +121,7 @@ export class VideosCommand extends AbstractCommand {
120 121
121 path, 122 path,
122 fields: { rating }, 123 fields: { rating },
124 headers: this.buildVideoPasswordHeader(videoPassword),
123 implicitToken: true, 125 implicitToken: true,
124 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 126 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
125 }) 127 })
@@ -151,6 +153,23 @@ export class VideosCommand extends AbstractCommand {
151 }) 153 })
152 } 154 }
153 155
156 getWithPassword (options: OverrideCommandOptions & {
157 id: number | string
158 password?: string
159 }) {
160 const path = '/api/v1/videos/' + options.id
161
162 return this.getRequestBody<VideoDetails>({
163 ...options,
164 headers:{
165 'x-peertube-video-password': options.password
166 },
167 path,
168 implicitToken: false,
169 defaultExpectedStatus: HttpStatusCode.OK_200
170 })
171 }
172
154 getSource (options: OverrideCommandOptions & { 173 getSource (options: OverrideCommandOptions & {
155 id: number | string 174 id: number | string
156 }) { 175 }) {
@@ -608,11 +627,13 @@ export class VideosCommand extends AbstractCommand {
608 nsfw?: boolean 627 nsfw?: boolean
609 privacy?: VideoPrivacy 628 privacy?: VideoPrivacy
610 fixture?: string 629 fixture?: string
630 videoPasswords?: string[]
611 }) { 631 }) {
612 const attributes: VideoEdit = { name: options.name } 632 const attributes: VideoEdit = { name: options.name }
613 if (options.nsfw) attributes.nsfw = options.nsfw 633 if (options.nsfw) attributes.nsfw = options.nsfw
614 if (options.privacy) attributes.privacy = options.privacy 634 if (options.privacy) attributes.privacy = options.privacy
615 if (options.fixture) attributes.fixture = options.fixture 635 if (options.fixture) attributes.fixture = options.fixture
636 if (options.videoPasswords) attributes.videoPasswords = options.videoPasswords
616 637
617 return this.upload({ ...options, attributes }) 638 return this.upload({ ...options, attributes })
618 } 639 }