From a4995eb7ac5745f62604d70f7b2225ff33916d49 Mon Sep 17 00:00:00 2001
From: Chocobozzz <me@florianbigard.com>
Date: Thu, 30 Apr 2020 10:03:09 +0200
Subject: [PATCH] Add ability to unregister plugin auths

---
 client/src/app/core/auth/auth.service.ts      |  1 -
 server/lib/plugins/register-helpers-store.ts  | 24 ++++++++++--
 .../main.js                                   | 10 ++++-
 .../main.js                                   | 10 ++++-
 server/tests/plugins/external-auth.ts         | 38 ++++++++++++++++++-
 server/tests/plugins/id-and-pass-auth.ts      | 26 ++++++++++++-
 .../plugins/register-server-option.model.ts   |  2 +
 7 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index e624c6a20..de8c509d1 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -181,7 +181,6 @@ export class AuthService {
       err => console.error(err)
     )
 
-
     this.user = null
 
     AuthUser.flush()
diff --git a/server/lib/plugins/register-helpers-store.ts b/server/lib/plugins/register-helpers-store.ts
index 6317ac2cf..a3ec7ef6a 100644
--- a/server/lib/plugins/register-helpers-store.ts
+++ b/server/lib/plugins/register-helpers-store.ts
@@ -49,8 +49,8 @@ export class RegisterHelpersStore {
 
   private readonly settings: RegisterServerSettingOptions[] = []
 
-  private readonly idAndPassAuths: RegisterServerAuthPassOptions[] = []
-  private readonly externalAuths: RegisterServerAuthExternalOptions[] = []
+  private idAndPassAuths: RegisterServerAuthPassOptions[] = []
+  private externalAuths: RegisterServerAuthExternalOptions[] = []
 
   private readonly onSettingsChangeCallbacks: ((settings: any) => void)[] = []
 
@@ -83,6 +83,8 @@ export class RegisterHelpersStore {
 
     const registerIdAndPassAuth = this.buildRegisterIdAndPassAuth()
     const registerExternalAuth = this.buildRegisterExternalAuth()
+    const unregisterIdAndPassAuth = this.buildUnregisterIdAndPassAuth()
+    const unregisterExternalAuth = this.buildUnregisterExternalAuth()
 
     const peertubeHelpers = buildPluginHelpers(this.npmName)
 
@@ -104,6 +106,8 @@ export class RegisterHelpersStore {
 
       registerIdAndPassAuth,
       registerExternalAuth,
+      unregisterIdAndPassAuth,
+      unregisterExternalAuth,
 
       peertubeHelpers
     }
@@ -179,7 +183,7 @@ export class RegisterHelpersStore {
   private buildRegisterIdAndPassAuth () {
     return (options: RegisterServerAuthPassOptions) => {
       if (!options.authName || typeof options.getWeight !== 'function' || typeof options.login !== 'function') {
-        logger.error('Cannot register auth plugin %s: authName of getWeight or login are not valid.', this.npmName)
+        logger.error('Cannot register auth plugin %s: authName, getWeight or login are not valid.', this.npmName, { options })
         return
       }
 
@@ -192,7 +196,7 @@ export class RegisterHelpersStore {
 
     return (options: RegisterServerAuthExternalOptions) => {
       if (!options.authName || typeof options.authDisplayName !== 'function' || typeof options.onAuthRequest !== 'function') {
-        logger.error('Cannot register auth plugin %s: authName of getWeight or login are not valid.', this.npmName)
+        logger.error('Cannot register auth plugin %s: authName, authDisplayName or onAuthRequest are not valid.', this.npmName, { options })
         return
       }
 
@@ -212,6 +216,18 @@ export class RegisterHelpersStore {
     }
   }
 
+  private buildUnregisterExternalAuth () {
+    return (authName: string) => {
+      this.externalAuths = this.externalAuths.filter(a => a.authName !== authName)
+    }
+  }
+
+  private buildUnregisterIdAndPassAuth () {
+    return (authName: string) => {
+      this.idAndPassAuths = this.idAndPassAuths.filter(a => a.authName !== authName)
+    }
+  }
+
   private buildSettingsManager (): PluginSettingsManager {
     return {
       getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name),
diff --git a/server/tests/fixtures/peertube-plugin-test-external-auth-one/main.js b/server/tests/fixtures/peertube-plugin-test-external-auth-one/main.js
index 91c67e550..c65b8d3a8 100644
--- a/server/tests/fixtures/peertube-plugin-test-external-auth-one/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-external-auth-one/main.js
@@ -1,6 +1,8 @@
 async function register ({
   registerExternalAuth,
-  peertubeHelpers
+  peertubeHelpers,
+  settingsManager,
+  unregisterExternalAuth
 }) {
   {
     const result = registerExternalAuth({
@@ -53,6 +55,12 @@ async function register ({
       }
     })
   }
+
+  settingsManager.onSettingsChange(settings => {
+    if (settings.disableKefka) {
+      unregisterExternalAuth('external-auth-2')
+    }
+  })
 }
 
 async function unregister () {
diff --git a/server/tests/fixtures/peertube-plugin-test-id-pass-auth-one/main.js b/server/tests/fixtures/peertube-plugin-test-id-pass-auth-one/main.js
index 9fc12a3e3..f58faa847 100644
--- a/server/tests/fixtures/peertube-plugin-test-id-pass-auth-one/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-id-pass-auth-one/main.js
@@ -1,6 +1,8 @@
 async function register ({
   registerIdAndPassAuth,
-  peertubeHelpers
+  peertubeHelpers,
+  settingsManager,
+  unregisterIdAndPassAuth
 }) {
   registerIdAndPassAuth({
     authName: 'spyro-auth',
@@ -47,6 +49,12 @@ async function register ({
       return null
     }
   })
+
+  settingsManager.onSettingsChange(settings => {
+    if (settings.disableSpyro) {
+      unregisterIdAndPassAuth('spyro-auth')
+    }
+  })
 }
 
 async function unregister () {
diff --git a/server/tests/plugins/external-auth.ts b/server/tests/plugins/external-auth.ts
index a72b2829b..312561538 100644
--- a/server/tests/plugins/external-auth.ts
+++ b/server/tests/plugins/external-auth.ts
@@ -16,7 +16,9 @@ import {
   setAccessTokensToServers,
   uninstallPlugin,
   updateMyUser,
-  wait
+  wait,
+  userLogin,
+  updatePluginSettings
 } from '../../../shared/extra-utils'
 import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
 
@@ -258,6 +260,40 @@ describe('Test external auth plugins', function () {
     await getMyUserInformation(server.url, kefkaAccessToken, 401)
   })
 
+  it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
+    await updatePluginSettings({
+      url: server.url,
+      accessToken: server.accessToken,
+      npmName: 'peertube-plugin-test-external-auth-one',
+      settings: { disableKefka: true }
+    })
+
+    await userLogin(server, { username: 'kefka', password: 'fake' }, 400)
+
+    await loginExternal({
+      server,
+      npmName: 'test-external-auth-one',
+      authName: 'external-auth-2',
+      query: {
+        username: 'kefka'
+      },
+      username: 'kefka',
+      statusCodeExpected: 404
+    })
+  })
+
+  it('Should have disabled this auth', async function () {
+    const res = await getConfig(server.url)
+
+    const config: ServerConfig = res.body
+
+    const auths = config.plugin.registeredExternalAuths
+    expect(auths).to.have.lengthOf(2)
+
+    const auth1 = auths.find(a => a.authName === 'external-auth-2')
+    expect(auth1).to.not.exist
+  })
+
   it('Should uninstall the plugin one and do not login Cyan', async function () {
     await uninstallPlugin({
       url: server.url,
diff --git a/server/tests/plugins/id-and-pass-auth.ts b/server/tests/plugins/id-and-pass-auth.ts
index 6c10730aa..97df4c1fd 100644
--- a/server/tests/plugins/id-and-pass-auth.ts
+++ b/server/tests/plugins/id-and-pass-auth.ts
@@ -12,7 +12,7 @@ import {
   updateMyUser,
   userLogin,
   wait,
-  login, refreshToken, getConfig
+  login, refreshToken, getConfig, updatePluginSettings
 } from '../../../shared/extra-utils'
 import { User, UserRole, ServerConfig } from '@shared/models'
 import { expect } from 'chai'
@@ -179,6 +179,30 @@ describe('Test id and pass auth plugins', function () {
     await waitUntilLog(server, 'valid email')
   })
 
+  it('Should unregister spyro-auth and do not login existing Spyro', async function () {
+    await updatePluginSettings({
+      url: server.url,
+      accessToken: server.accessToken,
+      npmName: 'peertube-plugin-test-id-pass-auth-one',
+      settings: { disableSpyro: true }
+    })
+
+    await userLogin(server, { username: 'spyro', password: 'spyro password' }, 400)
+    await userLogin(server, { username: 'spyro', password: 'fake' }, 400)
+  })
+
+  it('Should have disabled this auth', async function () {
+    const res = await getConfig(server.url)
+
+    const config: ServerConfig = res.body
+
+    const auths = config.plugin.registeredIdAndPassAuths
+    expect(auths).to.have.lengthOf(7)
+
+    const spyroAuth = auths.find(a => a.authName === 'spyro-auth')
+    expect(spyroAuth).to.not.exist
+  })
+
   it('Should uninstall the plugin one and do not login existing Crash', async function () {
     await uninstallPlugin({
       url: server.url,
diff --git a/server/typings/plugins/register-server-option.model.ts b/server/typings/plugins/register-server-option.model.ts
index c1e63316d..7f933b43a 100644
--- a/server/typings/plugins/register-server-option.model.ts
+++ b/server/typings/plugins/register-server-option.model.ts
@@ -49,6 +49,8 @@ export type RegisterServerOptions = {
 
   registerIdAndPassAuth: (options: RegisterServerAuthPassOptions) => void
   registerExternalAuth: (options: RegisterServerAuthExternalOptions) => RegisterServerAuthExternalResult
+  unregisterIdAndPassAuth: (authName: string) => void
+  unregisterExternalAuth: (authName: string) => void
 
   // Get plugin router to create custom routes
   // Base routes of this router are