]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/plugins/register-helpers-store.ts
Add ability to unregister plugin auths
[github/Chocobozzz/PeerTube.git] / server / lib / plugins / register-helpers-store.ts
1 import * as express from 'express'
2 import { logger } from '@server/helpers/logger'
3 import {
4 VIDEO_CATEGORIES,
5 VIDEO_LANGUAGES,
6 VIDEO_LICENCES,
7 VIDEO_PLAYLIST_PRIVACIES,
8 VIDEO_PRIVACIES
9 } from '@server/initializers/constants'
10 import { onExternalUserAuthenticated } from '@server/lib/auth'
11 import { PluginModel } from '@server/models/server/plugin'
12 import { RegisterServerOptions } from '@server/typings/plugins'
13 import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model'
14 import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model'
15 import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model'
16 import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model'
17 import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model'
18 import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model'
19 import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model'
20 import {
21 RegisterServerAuthExternalOptions,
22 RegisterServerAuthExternalResult,
23 RegisterServerAuthPassOptions,
24 RegisterServerExternalAuthenticatedResult
25 } from '@shared/models/plugins/register-server-auth.model'
26 import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model'
27 import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model'
28 import { serverHookObject } from '@shared/models/plugins/server-hook.model'
29 import { buildPluginHelpers } from './plugin-helpers'
30
31 type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
32 type VideoConstant = { [key in number | string]: string }
33
34 type UpdatedVideoConstant = {
35 [name in AlterableVideoConstant]: {
36 added: { key: number | string, label: string }[]
37 deleted: { key: number | string, label: string }[]
38 }
39 }
40
41 export class RegisterHelpersStore {
42 private readonly updatedVideoConstants: UpdatedVideoConstant = {
43 playlistPrivacy: { added: [], deleted: [] },
44 privacy: { added: [], deleted: [] },
45 language: { added: [], deleted: [] },
46 licence: { added: [], deleted: [] },
47 category: { added: [], deleted: [] }
48 }
49
50 private readonly settings: RegisterServerSettingOptions[] = []
51
52 private idAndPassAuths: RegisterServerAuthPassOptions[] = []
53 private externalAuths: RegisterServerAuthExternalOptions[] = []
54
55 private readonly onSettingsChangeCallbacks: ((settings: any) => void)[] = []
56
57 private readonly router: express.Router
58
59 constructor (
60 private readonly npmName: string,
61 private readonly plugin: PluginModel,
62 private readonly onHookAdded: (options: RegisterServerHookOptions) => void
63 ) {
64 this.router = express.Router()
65 }
66
67 buildRegisterHelpers (): RegisterServerOptions {
68 const registerHook = this.buildRegisterHook()
69 const registerSetting = this.buildRegisterSetting()
70
71 const getRouter = this.buildGetRouter()
72
73 const settingsManager = this.buildSettingsManager()
74 const storageManager = this.buildStorageManager()
75
76 const videoLanguageManager = this.buildVideoLanguageManager()
77
78 const videoLicenceManager = this.buildVideoLicenceManager()
79 const videoCategoryManager = this.buildVideoCategoryManager()
80
81 const videoPrivacyManager = this.buildVideoPrivacyManager()
82 const playlistPrivacyManager = this.buildPlaylistPrivacyManager()
83
84 const registerIdAndPassAuth = this.buildRegisterIdAndPassAuth()
85 const registerExternalAuth = this.buildRegisterExternalAuth()
86 const unregisterIdAndPassAuth = this.buildUnregisterIdAndPassAuth()
87 const unregisterExternalAuth = this.buildUnregisterExternalAuth()
88
89 const peertubeHelpers = buildPluginHelpers(this.npmName)
90
91 return {
92 registerHook,
93 registerSetting,
94
95 getRouter,
96
97 settingsManager,
98 storageManager,
99
100 videoLanguageManager,
101 videoCategoryManager,
102 videoLicenceManager,
103
104 videoPrivacyManager,
105 playlistPrivacyManager,
106
107 registerIdAndPassAuth,
108 registerExternalAuth,
109 unregisterIdAndPassAuth,
110 unregisterExternalAuth,
111
112 peertubeHelpers
113 }
114 }
115
116 reinitVideoConstants (npmName: string) {
117 const hash = {
118 language: VIDEO_LANGUAGES,
119 licence: VIDEO_LICENCES,
120 category: VIDEO_CATEGORIES,
121 privacy: VIDEO_PRIVACIES,
122 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
123 }
124 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
125
126 for (const type of types) {
127 const updatedConstants = this.updatedVideoConstants[type][npmName]
128 if (!updatedConstants) continue
129
130 for (const added of updatedConstants.added) {
131 delete hash[type][added.key]
132 }
133
134 for (const deleted of updatedConstants.deleted) {
135 hash[type][deleted.key] = deleted.label
136 }
137
138 delete this.updatedVideoConstants[type][npmName]
139 }
140 }
141
142 getSettings () {
143 return this.settings
144 }
145
146 getRouter () {
147 return this.router
148 }
149
150 getIdAndPassAuths () {
151 return this.idAndPassAuths
152 }
153
154 getExternalAuths () {
155 return this.externalAuths
156 }
157
158 getOnSettingsChangedCallbacks () {
159 return this.onSettingsChangeCallbacks
160 }
161
162 private buildGetRouter () {
163 return () => this.router
164 }
165
166 private buildRegisterSetting () {
167 return (options: RegisterServerSettingOptions) => {
168 this.settings.push(options)
169 }
170 }
171
172 private buildRegisterHook () {
173 return (options: RegisterServerHookOptions) => {
174 if (serverHookObject[options.target] !== true) {
175 logger.warn('Unknown hook %s of plugin %s. Skipping.', options.target, this.npmName)
176 return
177 }
178
179 return this.onHookAdded(options)
180 }
181 }
182
183 private buildRegisterIdAndPassAuth () {
184 return (options: RegisterServerAuthPassOptions) => {
185 if (!options.authName || typeof options.getWeight !== 'function' || typeof options.login !== 'function') {
186 logger.error('Cannot register auth plugin %s: authName, getWeight or login are not valid.', this.npmName, { options })
187 return
188 }
189
190 this.idAndPassAuths.push(options)
191 }
192 }
193
194 private buildRegisterExternalAuth () {
195 const self = this
196
197 return (options: RegisterServerAuthExternalOptions) => {
198 if (!options.authName || typeof options.authDisplayName !== 'function' || typeof options.onAuthRequest !== 'function') {
199 logger.error('Cannot register auth plugin %s: authName, authDisplayName or onAuthRequest are not valid.', this.npmName, { options })
200 return
201 }
202
203 this.externalAuths.push(options)
204
205 return {
206 userAuthenticated (result: RegisterServerExternalAuthenticatedResult): void {
207 onExternalUserAuthenticated({
208 npmName: self.npmName,
209 authName: options.authName,
210 authResult: result
211 }).catch(err => {
212 logger.error('Cannot execute onExternalUserAuthenticated.', { npmName: self.npmName, authName: options.authName, err })
213 })
214 }
215 } as RegisterServerAuthExternalResult
216 }
217 }
218
219 private buildUnregisterExternalAuth () {
220 return (authName: string) => {
221 this.externalAuths = this.externalAuths.filter(a => a.authName !== authName)
222 }
223 }
224
225 private buildUnregisterIdAndPassAuth () {
226 return (authName: string) => {
227 this.idAndPassAuths = this.idAndPassAuths.filter(a => a.authName !== authName)
228 }
229 }
230
231 private buildSettingsManager (): PluginSettingsManager {
232 return {
233 getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name),
234
235 getSettings: (names: string[]) => PluginModel.getSettings(this.plugin.name, this.plugin.type, names),
236
237 setSetting: (name: string, value: string) => PluginModel.setSetting(this.plugin.name, this.plugin.type, name, value),
238
239 onSettingsChange: (cb: (settings: any) => void) => this.onSettingsChangeCallbacks.push(cb)
240 }
241 }
242
243 private buildStorageManager (): PluginStorageManager {
244 return {
245 getData: (key: string) => PluginModel.getData(this.plugin.name, this.plugin.type, key),
246
247 storeData: (key: string, data: any) => PluginModel.storeData(this.plugin.name, this.plugin.type, key, data)
248 }
249 }
250
251 private buildVideoLanguageManager (): PluginVideoLanguageManager {
252 return {
253 addLanguage: (key: string, label: string) => {
254 return this.addConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label })
255 },
256
257 deleteLanguage: (key: string) => {
258 return this.deleteConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
259 }
260 }
261 }
262
263 private buildVideoCategoryManager (): PluginVideoCategoryManager {
264 return {
265 addCategory: (key: number, label: string) => {
266 return this.addConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label })
267 },
268
269 deleteCategory: (key: number) => {
270 return this.deleteConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
271 }
272 }
273 }
274
275 private buildVideoPrivacyManager (): PluginVideoPrivacyManager {
276 return {
277 deletePrivacy: (key: number) => {
278 return this.deleteConstant({ npmName: this.npmName, type: 'privacy', obj: VIDEO_PRIVACIES, key })
279 }
280 }
281 }
282
283 private buildPlaylistPrivacyManager (): PluginPlaylistPrivacyManager {
284 return {
285 deletePlaylistPrivacy: (key: number) => {
286 return this.deleteConstant({ npmName: this.npmName, type: 'playlistPrivacy', obj: VIDEO_PLAYLIST_PRIVACIES, key })
287 }
288 }
289 }
290
291 private buildVideoLicenceManager (): PluginVideoLicenceManager {
292 return {
293 addLicence: (key: number, label: string) => {
294 return this.addConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key, label })
295 },
296
297 deleteLicence: (key: number) => {
298 return this.deleteConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key })
299 }
300 }
301 }
302
303 private addConstant<T extends string | number> (parameters: {
304 npmName: string
305 type: AlterableVideoConstant
306 obj: VideoConstant
307 key: T
308 label: string
309 }) {
310 const { npmName, type, obj, key, label } = parameters
311
312 if (obj[key]) {
313 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
314 return false
315 }
316
317 if (!this.updatedVideoConstants[type][npmName]) {
318 this.updatedVideoConstants[type][npmName] = {
319 added: [],
320 deleted: []
321 }
322 }
323
324 this.updatedVideoConstants[type][npmName].added.push({ key, label })
325 obj[key] = label
326
327 return true
328 }
329
330 private deleteConstant<T extends string | number> (parameters: {
331 npmName: string
332 type: AlterableVideoConstant
333 obj: VideoConstant
334 key: T
335 }) {
336 const { npmName, type, obj, key } = parameters
337
338 if (!obj[key]) {
339 logger.warn('Cannot delete %s %s by plugin %s: key does not exist.', type, npmName, key)
340 return false
341 }
342
343 if (!this.updatedVideoConstants[type][npmName]) {
344 this.updatedVideoConstants[type][npmName] = {
345 added: [],
346 deleted: []
347 }
348 }
349
350 this.updatedVideoConstants[type][npmName].deleted.push({ key, label: obj[key] })
351 delete obj[key]
352
353 return true
354 }
355 }