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