"allowNumber": "true"
}
],
+ "@typescript-eslint/no-this-alias": [
+ "error",
+ {
+ "allowDestructuring": true, // Allow `const { props, state } = this`; false by default
+ "allowedNames": ["self"] // Allow `const self = this`; `[]` by default
+ }
+ ],
"@typescript-eslint/return-await": "off",
"@typescript-eslint/no-base-to-string": "off",
paginationValidator,
setDefaultPagination,
setDefaultSort,
- token,
userAutocompleteValidator,
usersAddValidator,
usersGetValidator,
import { UserRegister } from '../../../../shared/models/users/user-register.model'
import { MUser, MUserAccountDefault } from '@server/typings/models'
import { Hooks } from '@server/lib/plugins/hooks'
+import { handleIdAndPassLogin } from '@server/lib/auth'
const auditLogger = auditLoggerFactory('users')
usersRouter.post('/token',
loginRateLimiter,
- token,
+ handleIdAndPassLogin,
+ tokenSuccess
+)
+usersRouter.post('/token',
+ loginRateLimiter,
+ handleIdAndPassLogin,
+ tokenSuccess
+)
+usersRouter.post('/revoke-token',
+ loginRateLimiter,
+ handleIdAndPassLogin,
tokenSuccess
)
// TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route
--- /dev/null
+import * as express from 'express'
+import { OAUTH_LIFETIME } from '@server/initializers/constants'
+import * as OAuthServer from 'express-oauth-server'
+import { PluginManager } from '@server/lib/plugins/plugin-manager'
+import { RegisterServerAuthPassOptions } from '@shared/models/plugins/register-server-auth.model'
+import { logger } from '@server/helpers/logger'
+import { UserRole } from '@shared/models'
+
+const oAuthServer = new OAuthServer({
+ useErrorHandler: true,
+ accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN,
+ refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN,
+ continueMiddleware: true,
+ model: require('./oauth-model')
+})
+
+function onExternalAuthPlugin (npmName: string, username: string, email: string) {
+
+}
+
+async function handleIdAndPassLogin (req: express.Request, res: express.Response, next: express.NextFunction) {
+ const plugins = PluginManager.Instance.getIdAndPassAuths()
+ const pluginAuths: { npmName?: string, registerAuthOptions: RegisterServerAuthPassOptions }[] = []
+
+ for (const plugin of plugins) {
+ const auths = plugin.idAndPassAuths
+
+ for (const auth of auths) {
+ pluginAuths.push({
+ npmName: plugin.npmName,
+ registerAuthOptions: auth
+ })
+ }
+ }
+
+ pluginAuths.sort((a, b) => {
+ const aWeight = a.registerAuthOptions.getWeight()
+ const bWeight = b.registerAuthOptions.getWeight()
+
+ if (aWeight === bWeight) return 0
+ if (aWeight > bWeight) return 1
+ return -1
+ })
+
+ const loginOptions = {
+ id: req.body.username,
+ password: req.body.password
+ }
+
+ for (const pluginAuth of pluginAuths) {
+ logger.debug(
+ 'Using auth method of %s to login %s with weight %d.',
+ pluginAuth.npmName, loginOptions.id, pluginAuth.registerAuthOptions.getWeight()
+ )
+
+ const loginResult = await pluginAuth.registerAuthOptions.login(loginOptions)
+ if (loginResult) {
+ logger.info('Login success with plugin %s for %s.', pluginAuth.npmName, loginOptions.id)
+
+ res.locals.bypassLogin = {
+ bypass: true,
+ pluginName: pluginAuth.npmName,
+ user: {
+ username: loginResult.username,
+ email: loginResult.email,
+ role: loginResult.role || UserRole.USER,
+ displayName: loginResult.displayName || loginResult.username
+ }
+ }
+
+ break
+ }
+ }
+
+ return localLogin(req, res, next)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ oAuthServer,
+ handleIdAndPassLogin,
+ onExternalAuthPlugin
+}
+
+// ---------------------------------------------------------------------------
+
+function localLogin (req: express.Request, res: express.Response, next: express.NextFunction) {
+ return oAuthServer.token()(req, res, err => {
+ if (err) {
+ return res.status(err.status)
+ .json({
+ error: err.message,
+ code: err.name
+ })
+ .end()
+ }
+
+ return next()
+ })
+}
import * as Bluebird from 'bluebird'
+import * as express from 'express'
import { AccessDeniedError } from 'oauth2-server'
import { logger } from '../helpers/logger'
import { UserModel } from '../models/account/user'
import { CONFIG } from '../initializers/config'
import * as LRUCache from 'lru-cache'
import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
+import { MUser } from '@server/typings/models/user/user'
+import { UserAdminFlag } from '@shared/models/users/user-flag.model'
+import { createUserAccountAndChannelAndPlaylist } from './user'
+import { UserRole } from '@shared/models/users/user-role'
type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
if (accessTokenCache.has(bearerToken)) return Bluebird.resolve(accessTokenCache.get(bearerToken))
return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken)
- .then(tokenModel => {
- if (tokenModel) {
- accessTokenCache.set(bearerToken, tokenModel)
- userHavingToken.set(tokenModel.userId, tokenModel.accessToken)
- }
-
- return tokenModel
- })
+ .then(tokenModel => {
+ if (tokenModel) {
+ accessTokenCache.set(bearerToken, tokenModel)
+ userHavingToken.set(tokenModel.userId, tokenModel.accessToken)
+ }
+
+ return tokenModel
+ })
}
function getClient (clientId: string, clientSecret: string) {
}
async function getUser (usernameOrEmail: string, password: string) {
+ const res: express.Response = this.request.res
+ if (res.locals.bypassLogin && res.locals.bypassLogin.bypass === true) {
+ const obj = res.locals.bypassLogin
+ logger.info('Bypassing oauth login by plugin %s.', obj.pluginName)
+
+ let user = await UserModel.loadByEmail(obj.user.username)
+ if (!user) user = await createUserFromExternal(obj.pluginName, obj.user)
+
+ // This user does not belong to this plugin, skip it
+ if (user.pluginAuth !== obj.pluginName) return null
+
+ return user
+ }
+
logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).')
const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail)
token.destroy()
.catch(err => logger.error('Cannot destroy token when revoking token.', { err }))
+
+ return true
}
- /*
- * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js
- * "As per the discussion we need set older date
- * revokeToken will expected return a boolean in future version
- * https://github.com/oauthjs/node-oauth2-server/pull/274
- * https://github.com/oauthjs/node-oauth2-server/issues/290"
- */
- const expiredToken = token
- expiredToken.refreshTokenExpiresAt = new Date('2015-05-28T06:59:53.000Z')
-
- return expiredToken
+ return false
}
async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) {
revokeToken,
saveToken
}
+
+async function createUserFromExternal (pluginAuth: string, options: {
+ username: string
+ email: string
+ role: UserRole
+ displayName: string
+}) {
+ const userToCreate = new UserModel({
+ username: options.username,
+ password: null,
+ email: options.email,
+ nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
+ autoPlayVideo: true,
+ role: options.role,
+ videoQuota: CONFIG.USER.VIDEO_QUOTA,
+ videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
+ adminFlags: UserAdminFlag.NONE,
+ pluginAuth
+ }) as MUser
+
+ const { user } = await createUserAccountAndChannelAndPlaylist({
+ userToCreate,
+ userDisplayName: options.displayName
+ })
+
+ return user
+}
css: string[]
// Only if this is a plugin
+ registerHelpersStore?: RegisterHelpersStore
unregister?: Function
}
private static instance: PluginManager
private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
+
private hooks: { [name: string]: HookInformationValue[] } = {}
private translations: PluginLocalesTranslations = {}
- private registerHelpersStore: { [npmName: string]: RegisterHelpersStore } = {}
-
private constructor () {
}
return this.getRegisteredPluginsOrThemes(PluginType.THEME)
}
+ getIdAndPassAuths () {
+ return this.getRegisteredPlugins()
+ .map(p => ({ npmName: p.npmName, idAndPassAuths: p.registerHelpersStore.getIdAndPassAuths() }))
+ .filter(v => v.idAndPassAuths.length !== 0)
+ }
+
+ getExternalAuths () {
+ return this.getRegisteredPlugins()
+ .map(p => ({ npmName: p.npmName, externalAuths: p.registerHelpersStore.getExternalAuths() }))
+ .filter(v => v.externalAuths.length !== 0)
+ }
+
getRegisteredSettings (npmName: string) {
- const store = this.registerHelpersStore[npmName]
- if (store) return store.getSettings()
+ const result = this.getRegisteredPluginOrTheme(npmName)
+ if (!result || result.type !== PluginType.PLUGIN) return []
- return []
+ return result.registerHelpersStore.getSettings()
}
getRouter (npmName: string) {
- const store = this.registerHelpersStore[npmName]
- if (!store) return null
+ const result = this.getRegisteredPluginOrTheme(npmName)
+ if (!result || result.type !== PluginType.PLUGIN) return null
- return store.getRouter()
+ return result.registerHelpersStore.getRouter()
}
getTranslations (locale: string) {
this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName)
}
- const store = this.registerHelpersStore[plugin.npmName]
+ const store = plugin.registerHelpersStore
store.reinitVideoConstants(plugin.npmName)
- delete this.registerHelpersStore[plugin.npmName]
-
logger.info('Regenerating registered plugin CSS to global file.')
await this.regeneratePluginGlobalCSS()
}
this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, plugin.type)
let library: PluginLibrary
+ let registerHelpersStore: RegisterHelpersStore
if (plugin.type === PluginType.PLUGIN) {
- library = await this.registerPlugin(plugin, pluginPath, packageJSON)
+ const result = await this.registerPlugin(plugin, pluginPath, packageJSON)
+ library = result.library
+ registerHelpersStore = result.registerStore
}
const clientScripts: { [id: string]: ClientScript } = {}
staticDirs: packageJSON.staticDirs,
clientScripts,
css: packageJSON.css,
+ registerHelpersStore: registerHelpersStore || undefined,
unregister: library ? library.unregister : undefined
}
throw new Error('Library code is not valid (miss register or unregister function)')
}
- const registerHelpers = this.getRegisterHelpers(npmName, plugin)
- library.register(registerHelpers)
+ const { registerOptions, registerStore } = this.getRegisterHelpers(npmName, plugin)
+ library.register(registerOptions)
.catch(err => logger.error('Cannot register plugin %s.', npmName, { err }))
logger.info('Add plugin %s CSS to global file.', npmName)
await this.addCSSToGlobalFile(pluginPath, packageJSON.css)
- return library
+ return { library, registerStore }
}
// ###################### Translations ######################
// ###################### Generate register helpers ######################
- private getRegisterHelpers (npmName: string, plugin: PluginModel): RegisterServerOptions {
+ private getRegisterHelpers (
+ npmName: string,
+ plugin: PluginModel
+ ): { registerStore: RegisterHelpersStore, registerOptions: RegisterServerOptions } {
const onHookAdded = (options: RegisterServerHookOptions) => {
if (!this.hooks[options.target]) this.hooks[options.target] = []
}
const registerHelpersStore = new RegisterHelpersStore(npmName, plugin, onHookAdded.bind(this))
- this.registerHelpersStore[npmName] = registerHelpersStore
- return registerHelpersStore.buildRegisterHelpers()
+ return {
+ registerStore: registerHelpersStore,
+ registerOptions: registerHelpersStore.buildRegisterHelpers()
+ }
}
private sanitizeAndCheckPackageJSONOrThrow (packageJSON: PluginPackageJson, pluginType: PluginType) {
import * as express from 'express'
import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model'
import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model'
+import {
+ RegisterServerAuthExternalOptions,
+ RegisterServerAuthExternalResult,
+ RegisterServerAuthPassOptions
+} from '@shared/models/plugins/register-server-auth.model'
+import { onExternalAuthPlugin } from '@server/lib/auth'
type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
type VideoConstant = { [key in number | string]: string }
private readonly settings: RegisterServerSettingOptions[] = []
+ private readonly idAndPassAuths: RegisterServerAuthPassOptions[] = []
+ private readonly externalAuths: RegisterServerAuthExternalOptions[] = []
+
private readonly router: express.Router
constructor (
const videoPrivacyManager = this.buildVideoPrivacyManager()
const playlistPrivacyManager = this.buildPlaylistPrivacyManager()
+ const registerIdAndPassAuth = this.buildRegisterIdAndPassAuth()
+ const registerExternalAuth = this.buildRegisterExternalAuth()
+
const peertubeHelpers = buildPluginHelpers(this.npmName)
return {
videoPrivacyManager,
playlistPrivacyManager,
+ registerIdAndPassAuth,
+ registerExternalAuth,
+
peertubeHelpers
}
}
return this.router
}
+ getIdAndPassAuths () {
+ return this.idAndPassAuths
+ }
+
+ getExternalAuths () {
+ return this.externalAuths
+ }
+
private buildGetRouter () {
return () => this.router
}
}
}
+ private buildRegisterIdAndPassAuth () {
+ return (options: RegisterServerAuthPassOptions) => {
+ this.idAndPassAuths.push(options)
+ }
+ }
+
+ private buildRegisterExternalAuth () {
+ const self = this
+
+ return (options: RegisterServerAuthExternalOptions) => {
+ this.externalAuths.push(options)
+
+ return {
+ onAuth (options: { username: string, email: string }): void {
+ onExternalAuthPlugin(self.npmName, options.username, options.email)
+ }
+ } as RegisterServerAuthExternalResult
+ }
+ }
+
private buildSettingsManager (): PluginSettingsManager {
return {
getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name),
import { ActivityPubActorType } from '../../shared/models/activitypub'
import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants'
import { AccountModel } from '../models/account/account'
-import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
+import { buildActorInstance, setAsyncActorKeys } from './activitypub/actor'
import { createLocalVideoChannel } from './video-channel'
import { ActorModel } from '../models/activitypub/actor'
import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
import { Emailer } from './emailer'
import { MAccountDefault, MActorDefault, MChannelActor } from '../typings/models'
import { MUser, MUserDefault, MUserId } from '../typings/models/user'
+import { getAccountActivityPubUrl } from './activitypub/url'
type ChannelNames = { name: string, displayName: string }
import { v4 as uuidv4 } from 'uuid'
import { VideoChannelCreate } from '../../shared/models'
import { VideoChannelModel } from '../models/video/video-channel'
-import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
+import { buildActorInstance } from './activitypub/actor'
import { VideoModel } from '../models/video/video'
import { MAccountId, MChannelDefault, MChannelId } from '../typings/models'
+import { getVideoChannelActivityPubUrl } from './activitypub/url'
+import { federateVideoIfNeeded } from './activitypub/videos'
type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & { Account?: T }
import * as express from 'express'
-import * as OAuthServer from 'express-oauth-server'
-import { OAUTH_LIFETIME } from '../initializers/constants'
import { logger } from '../helpers/logger'
import { Socket } from 'socket.io'
import { getAccessToken } from '../lib/oauth-model'
-
-const oAuthServer = new OAuthServer({
- useErrorHandler: true,
- accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN,
- refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN,
- continueMiddleware: true,
- model: require('../lib/oauth-model')
-})
+import { handleIdAndPassLogin, oAuthServer } from '@server/lib/auth'
function authenticate (req: express.Request, res: express.Response, next: express.NextFunction, authenticateInQuery = false) {
const options = authenticateInQuery ? { allowBearerTokensInQueryString: true } : {}
return next()
}
-function token (req: express.Request, res: express.Response, next: express.NextFunction) {
- return oAuthServer.token()(req, res, err => {
- if (err) {
- return res.status(err.status)
- .json({
- error: err.message,
- code: err.name
- })
- .end()
- }
-
- return next()
- })
-}
-
// ---------------------------------------------------------------------------
export {
authenticate,
authenticateSocket,
authenticatePromiseIfNeeded,
- optionalAuthenticate,
- token
+ optionalAuthenticate
}
})
export class UserModel extends Model<UserModel> {
- @AllowNull(false)
+ @AllowNull(true)
@Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password'))
@Column
password: string
@Column
noWelcomeModal: boolean
+ @AllowNull(true)
+ @Default(null)
+ @Column
+ pluginAuth: string
+
@CreatedAt
createdAt: Date
getVideoAbusesList, updateCustomSubConfig, getCustomConfig, waitJobs
} from '../../../../shared/extra-utils'
import { follow } from '../../../../shared/extra-utils/server/follows'
-import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
+import { setAccessTokensToServers, logout } from '../../../../shared/extra-utils/users/login'
import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
import { CustomConfig } from '@shared/models/server'
})
describe('Logout', function () {
- it('Should logout (revoke token)')
+ it('Should logout (revoke token)', async function () {
+ await logout(server.url, server.accessToken)
+ })
- it('Should not be able to get the user information')
+ it('Should not be able to get the user information', async function () {
+ await getMyUserInformation(server.url, server.accessToken, 401)
+ })
- it('Should not be able to upload a video')
+ it('Should not be able to upload a video', async function () {
+ await uploadVideo(server.url, server.accessToken, { name: 'video' }, 401)
+ })
it('Should not be able to remove a video')
--- /dev/null
+async function register ({
+ registerIdAndPassAuth,
+ peertubeHelpers
+}) {
+ registerIdAndPassAuth({
+ type: 'id-and-pass',
+
+ onLogout: () => {
+ peertubeHelpers.logger.info('On logout for auth 1 - 1')
+ },
+
+ getWeight: () => 15,
+
+ login (body) {
+ if (body.id === 'spyro' && body.password === 'spyro password') {
+ return Promise.resolve({
+ username: 'spyro',
+ email: 'spyro@example.com',
+ role: 0,
+ displayName: 'Spyro the Dragon'
+ })
+ }
+
+ return null
+ }
+ })
+
+ registerIdAndPassAuth({
+ type: 'id-and-pass',
+
+ onLogout: () => {
+ peertubeHelpers.logger.info('On logout for auth 1 - 2')
+ },
+
+ getWeight: () => 50,
+
+ login (body) {
+ if (body.id === 'crash' && body.password === 'crash password') {
+ return Promise.resolve({
+ username: 'crash',
+ email: 'crash@example.com',
+ role: 2,
+ displayName: 'Crash Bandicoot'
+ })
+ }
+
+ return null
+ }
+ })
+}
+
+async function unregister () {
+ return
+}
+
+module.exports = {
+ register,
+ unregister
+}
+
+// ###########################################################################
--- /dev/null
+{
+ "name": "peertube-plugin-test-id-pass-auth-one",
+ "version": "0.0.1",
+ "description": "Id and pass auth one",
+ "engine": {
+ "peertube": ">=1.3.0"
+ },
+ "keywords": [
+ "peertube",
+ "plugin"
+ ],
+ "homepage": "https://github.com/Chocobozzz/PeerTube",
+ "author": "Chocobozzz",
+ "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
+ "library": "./main.js",
+ "staticDirs": {},
+ "css": [],
+ "clientScripts": [],
+ "translations": {}
+}
--- /dev/null
+async function register ({
+ registerIdAndPassAuth,
+ peertubeHelpers
+}) {
+ registerIdAndPassAuth({
+ type: 'id-and-pass',
+
+ onLogout: () => {
+ peertubeHelpers.logger.info('On logout for auth 3 - 1')
+ },
+
+ getWeight: () => 5,
+
+ login (body) {
+ if (body.id === 'laguna' && body.password === 'laguna password') {
+ return Promise.resolve({
+ username: 'laguna',
+ email: 'laguna@example.com',
+ displayName: 'Laguna Loire'
+ })
+ }
+
+ return null
+ }
+ })
+}
+
+async function unregister () {
+ return
+}
+
+module.exports = {
+ register,
+ unregister
+}
+
+// ###########################################################################
--- /dev/null
+{
+ "name": "peertube-plugin-test-id-pass-auth-three",
+ "version": "0.0.1",
+ "description": "Id and pass auth three",
+ "engine": {
+ "peertube": ">=1.3.0"
+ },
+ "keywords": [
+ "peertube",
+ "plugin"
+ ],
+ "homepage": "https://github.com/Chocobozzz/PeerTube",
+ "author": "Chocobozzz",
+ "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
+ "library": "./main.js",
+ "staticDirs": {},
+ "css": [],
+ "clientScripts": [],
+ "translations": {}
+}
--- /dev/null
+async function register ({
+ registerIdAndPassAuth,
+ peertubeHelpers
+}) {
+ registerIdAndPassAuth({
+ type: 'id-and-pass',
+
+ onLogout: () => {
+ peertubeHelpers.logger.info('On logout for auth 2 - 1')
+ },
+
+ getWeight: () => 30,
+
+ login (body) {
+ if (body.id === 'laguna' && body.password === 'laguna password') {
+ return Promise.resolve({
+ username: 'laguna',
+ email: 'laguna@example.com'
+ })
+ }
+
+ return null
+ }
+ })
+}
+
+async function unregister () {
+ return
+}
+
+module.exports = {
+ register,
+ unregister
+}
+
+// ###########################################################################
--- /dev/null
+{
+ "name": "peertube-plugin-test-id-pass-auth-two",
+ "version": "0.0.1",
+ "description": "Id and pass auth two",
+ "engine": {
+ "peertube": ">=1.3.0"
+ },
+ "keywords": [
+ "peertube",
+ "plugin"
+ ],
+ "homepage": "https://github.com/Chocobozzz/PeerTube",
+ "author": "Chocobozzz",
+ "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
+ "library": "./main.js",
+ "staticDirs": {},
+ "css": [],
+ "clientScripts": [],
+ "translations": {}
+}
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+
+import 'mocha'
+import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
+import { getPluginTestPath, installPlugin, setAccessTokensToServers } from '../../../shared/extra-utils'
+
+describe('Test id and pass auth plugins', function () {
+ let server: ServerInfo
+
+ before(async function () {
+ this.timeout(30000)
+
+ server = await flushAndRunServer(1)
+ await setAccessTokensToServers([ server ])
+
+ await installPlugin({
+ url: server.url,
+ accessToken: server.accessToken,
+ path: getPluginTestPath('-id-pass-auth-one')
+ })
+
+ await installPlugin({
+ url: server.url,
+ accessToken: server.accessToken,
+ path: getPluginTestPath('-id-pass-auth-two')
+ })
+ })
+
+ it('Should not login', async function() {
+
+ })
+
+ it('Should login Spyro, create the user and use the token', async function() {
+
+ })
+
+ it('Should login Crash, create the user and use the token', async function() {
+
+ })
+
+ it('Should login the first Laguna, create the user and use the token', async function() {
+
+ })
+
+ it('Should update Crash profile', async function () {
+
+ })
+
+ it('Should logout Crash', async function () {
+
+ // test token
+ })
+
+ it('Should have logged the Crash logout', async function () {
+
+ })
+
+ it('Should login Crash and keep the old existing profile', async function () {
+
+ })
+
+ it('Should uninstall the plugin one and do not login existing Crash', async function () {
+
+ })
+
+ after(async function () {
+ await cleanupTests([ server ])
+ })
+})
import './action-hooks'
+import './id-and-pass-auth'
import './filter-hooks'
import './translations'
import './video-constants'
import { MPlugin, MServer } from '@server/typings/models/server'
import { MServerBlocklist } from './models/server/server-blocklist'
import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
+import { UserRole } from '@shared/models'
declare module 'express' {
-
interface Response {
locals: {
+ bypassLogin?: {
+ bypass: boolean
+ pluginName: string
+ user: {
+ username: string
+ email: string
+ displayName: string
+ role: UserRole
+ }
+ }
+
videoAll?: MVideoFullLight
onlyImmutableVideo?: MVideoImmutable
onlyVideo?: MVideoThumbnail
import { Router } from 'express'
import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model'
import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model'
+import { RegisterServerAuthPassOptions, RegisterServerAuthExternalOptions, RegisterServerAuthExternalResult } from '@shared/models/plugins/register-server-auth.model'
export type PeerTubeHelpers = {
logger: Logger
videoPrivacyManager: PluginVideoPrivacyManager
playlistPrivacyManager: PluginPlaylistPrivacyManager
+ registerIdAndPassAuth: (options: RegisterServerAuthPassOptions) => void
+ registerExternalAuth: (options: RegisterServerAuthExternalOptions) => RegisterServerAuthExternalResult
+
// Get plugin router to create custom routes
// Base routes of this router are
// * /plugins/:pluginName/:pluginVersion/router/...
.expect(expectedStatus)
}
+function logout (url: string, token: string, expectedStatus = 200) {
+ const path = '/api/v1/users/revoke-token'
+
+ return request(url)
+ .post(path)
+ .set('Authorization', 'Bearer ' + token)
+ .type('form')
+ .expect(expectedStatus)
+}
+
async function serverLogin (server: Server) {
const res = await login(server.url, server.client, server.user, 200)
export {
login,
+ logout,
serverLogin,
userLogin,
getAccessToken,
--- /dev/null
+import { UserRole } from '@shared/models'
+
+export type RegisterServerAuthOptions = RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions
+
+export interface RegisterServerAuthPassOptions {
+ type: 'id-and-pass'
+
+ onLogout?: Function
+
+ getWeight(): number
+
+ // Used by PeerTube to login a user
+ // Returns null if the login failed, or { username, email } on success
+ login(body: {
+ id: string
+ password: string
+ }): Promise<{
+ username: string
+ email: string
+ role?: UserRole
+ displayName?: string
+ } | null>
+}
+
+export interface RegisterServerAuthExternalOptions {
+ type: 'external'
+
+ onLogout?: Function
+}
+
+export interface RegisterServerAuthExternalResult {
+ onAuth (options: { username: string, email: string }): void
+}