External auth can update user on login
This commit is contained in:
parent
7e0c26066a
commit
60b880acdf
|
@ -8,10 +8,11 @@ function isVideoCaptionLanguageValid (value: any) {
|
||||||
return exists(value) && VIDEO_LANGUAGES[value] !== undefined
|
return exists(value) && VIDEO_LANGUAGES[value] !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoCaptionTypesRegex = Object.keys(MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT)
|
// MacOS sends application/octet-stream
|
||||||
.concat([ 'application/octet-stream' ]) // MacOS sends application/octet-stream
|
const videoCaptionTypesRegex = [ ...Object.keys(MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT), 'application/octet-stream' ]
|
||||||
.map(m => `(${m})`)
|
.map(m => `(${m})`)
|
||||||
.join('|')
|
.join('|')
|
||||||
|
|
||||||
function isVideoCaptionFile (files: UploadFilesForCheck, field: string) {
|
function isVideoCaptionFile (files: UploadFilesForCheck, field: string) {
|
||||||
return isFileValid({
|
return isFileValid({
|
||||||
files,
|
files,
|
||||||
|
|
|
@ -22,10 +22,11 @@ function isVideoImportStateValid (value: any) {
|
||||||
return exists(value) && VIDEO_IMPORT_STATES[value] !== undefined
|
return exists(value) && VIDEO_IMPORT_STATES[value] !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoTorrentImportRegex = Object.keys(MIMETYPES.TORRENT.MIMETYPE_EXT)
|
// MacOS sends application/octet-stream
|
||||||
.concat([ 'application/octet-stream' ]) // MacOS sends application/octet-stream
|
const videoTorrentImportRegex = [ ...Object.keys(MIMETYPES.TORRENT.MIMETYPE_EXT), 'application/octet-stream' ]
|
||||||
.map(m => `(${m})`)
|
.map(m => `(${m})`)
|
||||||
.join('|')
|
.join('|')
|
||||||
|
|
||||||
function isVideoImportTorrentFile (files: UploadFilesForCheck) {
|
function isVideoImportTorrentFile (files: UploadFilesForCheck) {
|
||||||
return isFileValid({
|
return isFileValid({
|
||||||
files,
|
files,
|
||||||
|
|
|
@ -174,7 +174,8 @@ function checkRemoteRedundancyConfig () {
|
||||||
function checkStorageConfig () {
|
function checkStorageConfig () {
|
||||||
// Check storage directory locations
|
// Check storage directory locations
|
||||||
if (isProdInstance()) {
|
if (isProdInstance()) {
|
||||||
const configStorage = config.get('storage')
|
const configStorage = config.get<{ [ name: string ]: string }>('storage')
|
||||||
|
|
||||||
for (const key of Object.keys(configStorage)) {
|
for (const key of Object.keys(configStorage)) {
|
||||||
if (configStorage[key].startsWith('storage/')) {
|
if (configStorage[key].startsWith('storage/')) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
|
|
@ -51,8 +51,7 @@ function removeCacheAndTmpDirectories () {
|
||||||
const tasks: Promise<any>[] = []
|
const tasks: Promise<any>[] = []
|
||||||
|
|
||||||
// Cache directories
|
// Cache directories
|
||||||
for (const key of Object.keys(cacheDirectories)) {
|
for (const dir of cacheDirectories) {
|
||||||
const dir = cacheDirectories[key]
|
|
||||||
tasks.push(removeDirectoryOrContent(dir))
|
tasks.push(removeDirectoryOrContent(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +86,7 @@ function createDirectoriesIfNotExist () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache directories
|
// Cache directories
|
||||||
for (const key of Object.keys(cacheDirectories)) {
|
for (const dir of cacheDirectories) {
|
||||||
const dir = cacheDirectories[key]
|
|
||||||
tasks.push(ensureDir(dir))
|
tasks.push(ensureDir(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
RegisterServerExternalAuthenticatedResult
|
RegisterServerExternalAuthenticatedResult
|
||||||
} from '@server/types/plugins/register-server-auth.model'
|
} from '@server/types/plugins/register-server-auth.model'
|
||||||
import { UserAdminFlag, UserRole } from '@shared/models'
|
import { UserAdminFlag, UserRole } from '@shared/models'
|
||||||
|
import { BypassLogin } from './oauth-model'
|
||||||
|
|
||||||
export type ExternalUser =
|
export type ExternalUser =
|
||||||
Pick<MUser, 'username' | 'email' | 'role' | 'adminFlags' | 'videoQuotaDaily' | 'videoQuota'> &
|
Pick<MUser, 'username' | 'email' | 'role' | 'adminFlags' | 'videoQuotaDaily' | 'videoQuota'> &
|
||||||
|
@ -28,6 +29,7 @@ export type ExternalUser =
|
||||||
const authBypassTokens = new Map<string, {
|
const authBypassTokens = new Map<string, {
|
||||||
expires: Date
|
expires: Date
|
||||||
user: ExternalUser
|
user: ExternalUser
|
||||||
|
userUpdater: RegisterServerAuthenticatedResult['userUpdater']
|
||||||
authName: string
|
authName: string
|
||||||
npmName: string
|
npmName: string
|
||||||
}>()
|
}>()
|
||||||
|
@ -63,7 +65,8 @@ async function onExternalUserAuthenticated (options: {
|
||||||
expires,
|
expires,
|
||||||
user,
|
user,
|
||||||
npmName,
|
npmName,
|
||||||
authName
|
authName,
|
||||||
|
userUpdater: authResult.userUpdater
|
||||||
})
|
})
|
||||||
|
|
||||||
// Cleanup expired tokens
|
// Cleanup expired tokens
|
||||||
|
@ -85,7 +88,7 @@ async function getAuthNameFromRefreshGrant (refreshToken?: string) {
|
||||||
return tokenModel?.authName
|
return tokenModel?.authName
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getBypassFromPasswordGrant (username: string, password: string) {
|
async function getBypassFromPasswordGrant (username: string, password: string): Promise<BypassLogin> {
|
||||||
const plugins = PluginManager.Instance.getIdAndPassAuths()
|
const plugins = PluginManager.Instance.getIdAndPassAuths()
|
||||||
const pluginAuths: { npmName?: string, registerAuthOptions: RegisterServerAuthPassOptions }[] = []
|
const pluginAuths: { npmName?: string, registerAuthOptions: RegisterServerAuthPassOptions }[] = []
|
||||||
|
|
||||||
|
@ -140,7 +143,8 @@ async function getBypassFromPasswordGrant (username: string, password: string) {
|
||||||
bypass: true,
|
bypass: true,
|
||||||
pluginName: pluginAuth.npmName,
|
pluginName: pluginAuth.npmName,
|
||||||
authName: authOptions.authName,
|
authName: authOptions.authName,
|
||||||
user: buildUserResult(loginResult)
|
user: buildUserResult(loginResult),
|
||||||
|
userUpdater: loginResult.userUpdater
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Error in auth method %s of plugin %s', authOptions.authName, pluginAuth.npmName, { err })
|
logger.error('Error in auth method %s of plugin %s', authOptions.authName, pluginAuth.npmName, { err })
|
||||||
|
@ -150,7 +154,7 @@ async function getBypassFromPasswordGrant (username: string, password: string) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBypassFromExternalAuth (username: string, externalAuthToken: string) {
|
function getBypassFromExternalAuth (username: string, externalAuthToken: string): BypassLogin {
|
||||||
const obj = authBypassTokens.get(externalAuthToken)
|
const obj = authBypassTokens.get(externalAuthToken)
|
||||||
if (!obj) throw new Error('Cannot authenticate user with unknown bypass token')
|
if (!obj) throw new Error('Cannot authenticate user with unknown bypass token')
|
||||||
|
|
||||||
|
@ -174,6 +178,7 @@ function getBypassFromExternalAuth (username: string, externalAuthToken: string)
|
||||||
bypass: true,
|
bypass: true,
|
||||||
pluginName: npmName,
|
pluginName: npmName,
|
||||||
authName,
|
authName,
|
||||||
|
userUpdater: obj.userUpdater,
|
||||||
user
|
user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,6 +199,11 @@ function isAuthResultValid (npmName: string, authName: string, result: RegisterS
|
||||||
if (result.videoQuota && !isUserVideoQuotaValid(result.videoQuota + '')) return returnError('videoQuota')
|
if (result.videoQuota && !isUserVideoQuotaValid(result.videoQuota + '')) return returnError('videoQuota')
|
||||||
if (result.videoQuotaDaily && !isUserVideoQuotaDailyValid(result.videoQuotaDaily + '')) return returnError('videoQuotaDaily')
|
if (result.videoQuotaDaily && !isUserVideoQuotaDailyValid(result.videoQuotaDaily + '')) return returnError('videoQuotaDaily')
|
||||||
|
|
||||||
|
if (result.userUpdater && typeof result.userUpdater !== 'function') {
|
||||||
|
logger.error('Auth method %s of plugin %s did not provide a valid user updater function.', authName, npmName)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { AccessDeniedError } from '@node-oauth/oauth2-server'
|
import { AccessDeniedError } from '@node-oauth/oauth2-server'
|
||||||
import { PluginManager } from '@server/lib/plugins/plugin-manager'
|
import { PluginManager } from '@server/lib/plugins/plugin-manager'
|
||||||
|
import { AccountModel } from '@server/models/account/account'
|
||||||
|
import { AuthenticatedResultUpdaterFieldName, RegisterServerAuthenticatedResult } from '@server/types'
|
||||||
import { MOAuthClient } from '@server/types/models'
|
import { MOAuthClient } from '@server/types/models'
|
||||||
import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token'
|
import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token'
|
||||||
import { MUser } from '@server/types/models/user/user'
|
import { MUser, MUserDefault } from '@server/types/models/user/user'
|
||||||
import { pick } from '@shared/core-utils'
|
import { pick } from '@shared/core-utils'
|
||||||
|
import { AttributesOnly } from '@shared/typescript-utils'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { CONFIG } from '../../initializers/config'
|
import { CONFIG } from '../../initializers/config'
|
||||||
import { OAuthClientModel } from '../../models/oauth/oauth-client'
|
import { OAuthClientModel } from '../../models/oauth/oauth-client'
|
||||||
|
@ -27,6 +30,7 @@ export type BypassLogin = {
|
||||||
pluginName: string
|
pluginName: string
|
||||||
authName?: string
|
authName?: string
|
||||||
user: ExternalUser
|
user: ExternalUser
|
||||||
|
userUpdater: RegisterServerAuthenticatedResult['userUpdater']
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAccessToken (bearerToken: string) {
|
async function getAccessToken (bearerToken: string) {
|
||||||
|
@ -84,7 +88,9 @@ async function getUser (usernameOrEmail?: string, password?: string, bypassLogin
|
||||||
logger.info('Bypassing oauth login by plugin %s.', bypassLogin.pluginName)
|
logger.info('Bypassing oauth login by plugin %s.', bypassLogin.pluginName)
|
||||||
|
|
||||||
let user = await UserModel.loadByEmail(bypassLogin.user.email)
|
let user = await UserModel.loadByEmail(bypassLogin.user.email)
|
||||||
|
|
||||||
if (!user) user = await createUserFromExternal(bypassLogin.pluginName, bypassLogin.user)
|
if (!user) user = await createUserFromExternal(bypassLogin.pluginName, bypassLogin.user)
|
||||||
|
else user = await updateUserFromExternal(user, bypassLogin.user, bypassLogin.userUpdater)
|
||||||
|
|
||||||
// Cannot create a user
|
// Cannot create a user
|
||||||
if (!user) throw new AccessDeniedError('Cannot create such user: an actor with that name already exists.')
|
if (!user) throw new AccessDeniedError('Cannot create such user: an actor with that name already exists.')
|
||||||
|
@ -234,6 +240,51 @@ async function createUserFromExternal (pluginAuth: string, userOptions: External
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateUserFromExternal (
|
||||||
|
user: MUserDefault,
|
||||||
|
userOptions: ExternalUser,
|
||||||
|
userUpdater: RegisterServerAuthenticatedResult['userUpdater']
|
||||||
|
) {
|
||||||
|
if (!userUpdater) return user
|
||||||
|
|
||||||
|
{
|
||||||
|
type UserAttributeKeys = keyof AttributesOnly<UserModel>
|
||||||
|
const mappingKeys: { [ id in UserAttributeKeys ]?: AuthenticatedResultUpdaterFieldName } = {
|
||||||
|
role: 'role',
|
||||||
|
adminFlags: 'adminFlags',
|
||||||
|
videoQuota: 'videoQuota',
|
||||||
|
videoQuotaDaily: 'videoQuotaDaily'
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const modelKey of Object.keys(mappingKeys)) {
|
||||||
|
const pluginOptionKey = mappingKeys[modelKey]
|
||||||
|
|
||||||
|
const newValue = userUpdater({ fieldName: pluginOptionKey, currentValue: user[modelKey], newValue: userOptions[pluginOptionKey] })
|
||||||
|
user.set(modelKey, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
type AccountAttributeKeys = keyof Partial<AttributesOnly<AccountModel>>
|
||||||
|
const mappingKeys: { [ id in AccountAttributeKeys ]?: AuthenticatedResultUpdaterFieldName } = {
|
||||||
|
name: 'displayName'
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const modelKey of Object.keys(mappingKeys)) {
|
||||||
|
const optionKey = mappingKeys[modelKey]
|
||||||
|
|
||||||
|
const newValue = userUpdater({ fieldName: optionKey, currentValue: user.Account[modelKey], newValue: userOptions[optionKey] })
|
||||||
|
user.Account.set(modelKey, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Updated user %s with plugin userUpdated function.', user.email, { user, userOptions })
|
||||||
|
|
||||||
|
user.Account = await user.Account.save()
|
||||||
|
|
||||||
|
return user.save()
|
||||||
|
}
|
||||||
|
|
||||||
function checkUserValidityOrThrow (user: MUser) {
|
function checkUserValidityOrThrow (user: MUser) {
|
||||||
if (user.blocked) throw new AccessDeniedError('User is blocked.')
|
if (user.blocked) throw new AccessDeniedError('User is blocked.')
|
||||||
}
|
}
|
||||||
|
|
|
@ -439,7 +439,7 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
if (!element) return videoFile.save({ transaction })
|
if (!element) return videoFile.save({ transaction })
|
||||||
|
|
||||||
for (const k of Object.keys(videoFile.toJSON())) {
|
for (const k of Object.keys(videoFile.toJSON())) {
|
||||||
element[k] = videoFile[k]
|
element.set(k, videoFile[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
return element.save({ transaction })
|
return element.save({ transaction })
|
||||||
|
|
|
@ -36,7 +36,14 @@ async function register ({
|
||||||
displayName: 'Kefka Palazzo',
|
displayName: 'Kefka Palazzo',
|
||||||
adminFlags: 1,
|
adminFlags: 1,
|
||||||
videoQuota: 42000,
|
videoQuota: 42000,
|
||||||
videoQuotaDaily: 42100
|
videoQuotaDaily: 42100,
|
||||||
|
|
||||||
|
// Always use new value except for videoQuotaDaily field
|
||||||
|
userUpdater: ({ fieldName, currentValue, newValue }) => {
|
||||||
|
if (fieldName === 'videoQuotaDaily') return currentValue
|
||||||
|
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
hookTokenValidity: (options) => {
|
hookTokenValidity: (options) => {
|
||||||
|
|
|
@ -33,7 +33,18 @@ async function register ({
|
||||||
if (body.id === 'laguna' && body.password === 'laguna password') {
|
if (body.id === 'laguna' && body.password === 'laguna password') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
username: 'laguna',
|
username: 'laguna',
|
||||||
email: 'laguna@example.com'
|
email: 'laguna@example.com',
|
||||||
|
displayName: 'Laguna Loire',
|
||||||
|
adminFlags: 1,
|
||||||
|
videoQuota: 42000,
|
||||||
|
videoQuotaDaily: 42100,
|
||||||
|
|
||||||
|
// Always use new value except for videoQuotaDaily field
|
||||||
|
userUpdater: ({ fieldName, currentValue, newValue }) => {
|
||||||
|
if (fieldName === 'videoQuotaDaily') return currentValue
|
||||||
|
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ describe('Test external auth plugins', function () {
|
||||||
|
|
||||||
let kefkaAccessToken: string
|
let kefkaAccessToken: string
|
||||||
let kefkaRefreshToken: string
|
let kefkaRefreshToken: string
|
||||||
|
let kefkaId: number
|
||||||
|
|
||||||
let externalAuthToken: string
|
let externalAuthToken: string
|
||||||
|
|
||||||
|
@ -184,6 +185,8 @@ describe('Test external auth plugins', function () {
|
||||||
expect(body.adminFlags).to.equal(UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST)
|
expect(body.adminFlags).to.equal(UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST)
|
||||||
expect(body.videoQuota).to.equal(42000)
|
expect(body.videoQuota).to.equal(42000)
|
||||||
expect(body.videoQuotaDaily).to.equal(42100)
|
expect(body.videoQuotaDaily).to.equal(42100)
|
||||||
|
|
||||||
|
kefkaId = body.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -246,6 +249,37 @@ describe('Test external auth plugins', function () {
|
||||||
expect(body.role.id).to.equal(UserRole.USER)
|
expect(body.role.id).to.equal(UserRole.USER)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should login Kefka and update the profile', async function () {
|
||||||
|
{
|
||||||
|
await server.users.update({ userId: kefkaId, videoQuota: 43000, videoQuotaDaily: 43100 })
|
||||||
|
await server.users.updateMe({ token: kefkaAccessToken, displayName: 'kefka updated' })
|
||||||
|
|
||||||
|
const body = await server.users.getMyInfo({ token: kefkaAccessToken })
|
||||||
|
expect(body.username).to.equal('kefka')
|
||||||
|
expect(body.account.displayName).to.equal('kefka updated')
|
||||||
|
expect(body.videoQuota).to.equal(43000)
|
||||||
|
expect(body.videoQuotaDaily).to.equal(43100)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const res = await loginExternal({
|
||||||
|
server,
|
||||||
|
npmName: 'test-external-auth-one',
|
||||||
|
authName: 'external-auth-2',
|
||||||
|
username: 'kefka'
|
||||||
|
})
|
||||||
|
|
||||||
|
kefkaAccessToken = res.access_token
|
||||||
|
kefkaRefreshToken = res.refresh_token
|
||||||
|
|
||||||
|
const body = await server.users.getMyInfo({ token: kefkaAccessToken })
|
||||||
|
expect(body.username).to.equal('kefka')
|
||||||
|
expect(body.account.displayName).to.equal('Kefka Palazzo')
|
||||||
|
expect(body.videoQuota).to.equal(42000)
|
||||||
|
expect(body.videoQuotaDaily).to.equal(43100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
it('Should not update an external auth email', async function () {
|
it('Should not update an external auth email', async function () {
|
||||||
await server.users.updateMe({
|
await server.users.updateMe({
|
||||||
token: cyanAccessToken,
|
token: cyanAccessToken,
|
||||||
|
|
|
@ -13,6 +13,7 @@ describe('Test id and pass auth plugins', function () {
|
||||||
|
|
||||||
let lagunaAccessToken: string
|
let lagunaAccessToken: string
|
||||||
let lagunaRefreshToken: string
|
let lagunaRefreshToken: string
|
||||||
|
let lagunaId: number
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
this.timeout(30000)
|
this.timeout(30000)
|
||||||
|
@ -78,8 +79,10 @@ describe('Test id and pass auth plugins', function () {
|
||||||
const body = await server.users.getMyInfo({ token: lagunaAccessToken })
|
const body = await server.users.getMyInfo({ token: lagunaAccessToken })
|
||||||
|
|
||||||
expect(body.username).to.equal('laguna')
|
expect(body.username).to.equal('laguna')
|
||||||
expect(body.account.displayName).to.equal('laguna')
|
expect(body.account.displayName).to.equal('Laguna Loire')
|
||||||
expect(body.role.id).to.equal(UserRole.USER)
|
expect(body.role.id).to.equal(UserRole.USER)
|
||||||
|
|
||||||
|
lagunaId = body.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -132,6 +135,33 @@ describe('Test id and pass auth plugins', function () {
|
||||||
expect(body.role.id).to.equal(UserRole.MODERATOR)
|
expect(body.role.id).to.equal(UserRole.MODERATOR)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should login Laguna and update the profile', async function () {
|
||||||
|
{
|
||||||
|
await server.users.update({ userId: lagunaId, videoQuota: 43000, videoQuotaDaily: 43100 })
|
||||||
|
await server.users.updateMe({ token: lagunaAccessToken, displayName: 'laguna updated' })
|
||||||
|
|
||||||
|
const body = await server.users.getMyInfo({ token: lagunaAccessToken })
|
||||||
|
expect(body.username).to.equal('laguna')
|
||||||
|
expect(body.account.displayName).to.equal('laguna updated')
|
||||||
|
expect(body.videoQuota).to.equal(43000)
|
||||||
|
expect(body.videoQuotaDaily).to.equal(43100)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const body = await server.login.login({ user: { username: 'laguna', password: 'laguna password' } })
|
||||||
|
lagunaAccessToken = body.access_token
|
||||||
|
lagunaRefreshToken = body.refresh_token
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const body = await server.users.getMyInfo({ token: lagunaAccessToken })
|
||||||
|
expect(body.username).to.equal('laguna')
|
||||||
|
expect(body.account.displayName).to.equal('Laguna Loire')
|
||||||
|
expect(body.videoQuota).to.equal(42000)
|
||||||
|
expect(body.videoQuotaDaily).to.equal(43100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
it('Should reject token of laguna by the plugin hook', async function () {
|
it('Should reject token of laguna by the plugin hook', async function () {
|
||||||
this.timeout(10000)
|
this.timeout(10000)
|
||||||
|
|
||||||
|
@ -147,7 +177,7 @@ describe('Test id and pass auth plugins', function () {
|
||||||
await server.servers.waitUntilLog('valid username')
|
await server.servers.waitUntilLog('valid username')
|
||||||
|
|
||||||
await command.login({ user: { username: 'kiros', password: 'kiros password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
await command.login({ user: { username: 'kiros', password: 'kiros password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||||
await server.servers.waitUntilLog('valid display name')
|
await server.servers.waitUntilLog('valid displayName')
|
||||||
|
|
||||||
await command.login({ user: { username: 'raine', password: 'raine password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
await command.login({ user: { username: 'raine', password: 'raine password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||||
await server.servers.waitUntilLog('valid role')
|
await server.servers.waitUntilLog('valid role')
|
||||||
|
|
1
server/types/express.d.ts
vendored
1
server/types/express.d.ts
vendored
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { OutgoingHttpHeaders } from 'http'
|
import { OutgoingHttpHeaders } from 'http'
|
||||||
import { RegisterServerAuthExternalOptions } from '@server/types'
|
import { RegisterServerAuthExternalOptions } from '@server/types'
|
||||||
import {
|
import {
|
||||||
|
|
12
server/types/lib.d.ts
vendored
Normal file
12
server/types/lib.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
type ObjectKeys<T> =
|
||||||
|
T extends object
|
||||||
|
? `${Exclude<keyof T, symbol>}`[]
|
||||||
|
: T extends number
|
||||||
|
? []
|
||||||
|
: T extends any | string
|
||||||
|
? string[]
|
||||||
|
: never
|
||||||
|
|
||||||
|
interface ObjectConstructor {
|
||||||
|
keys<T> (o: T): ObjectKeys<T>
|
||||||
|
}
|
|
@ -4,15 +4,29 @@ import { MOAuthToken, MUser } from '../models'
|
||||||
|
|
||||||
export type RegisterServerAuthOptions = RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions
|
export type RegisterServerAuthOptions = RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions
|
||||||
|
|
||||||
|
export type AuthenticatedResultUpdaterFieldName = 'displayName' | 'role' | 'adminFlags' | 'videoQuota' | 'videoQuotaDaily'
|
||||||
|
|
||||||
export interface RegisterServerAuthenticatedResult {
|
export interface RegisterServerAuthenticatedResult {
|
||||||
|
// Update the user profile if it already exists
|
||||||
|
// Default behaviour is no update
|
||||||
|
// Introduced in PeerTube >= 5.1
|
||||||
|
userUpdater?: <T> (options: {
|
||||||
|
fieldName: AuthenticatedResultUpdaterFieldName
|
||||||
|
currentValue: T
|
||||||
|
newValue: T
|
||||||
|
}) => T
|
||||||
|
|
||||||
username: string
|
username: string
|
||||||
email: string
|
email: string
|
||||||
role?: UserRole
|
role?: UserRole
|
||||||
displayName?: string
|
displayName?: string
|
||||||
|
|
||||||
|
// PeerTube >= 5.1
|
||||||
adminFlags?: UserAdminFlag
|
adminFlags?: UserAdminFlag
|
||||||
|
|
||||||
|
// PeerTube >= 5.1
|
||||||
videoQuota?: number
|
videoQuota?: number
|
||||||
|
// PeerTube >= 5.1
|
||||||
videoQuotaDaily?: number
|
videoQuotaDaily?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -433,7 +433,27 @@ function register (...) {
|
||||||
username: 'user'
|
username: 'user'
|
||||||
email: 'user@example.com'
|
email: 'user@example.com'
|
||||||
role: 2
|
role: 2
|
||||||
displayName: 'User display name'
|
displayName: 'User display name',
|
||||||
|
|
||||||
|
// Custom admin flags (bypass video auto moderation etc.)
|
||||||
|
// https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/users/user-flag.model.ts
|
||||||
|
// PeerTube >= 5.1
|
||||||
|
adminFlags: 0,
|
||||||
|
// Quota in bytes
|
||||||
|
// PeerTube >= 5.1
|
||||||
|
videoQuota: 1024 * 1024 * 1024, // 1GB
|
||||||
|
// PeerTube >= 5.1
|
||||||
|
videoQuotaDaily: -1, // Unlimited
|
||||||
|
|
||||||
|
// Update the user profile if it already exists
|
||||||
|
// Default behaviour is no update
|
||||||
|
// Introduced in PeerTube >= 5.1
|
||||||
|
userUpdater: ({ fieldName, currentValue, newValue }) => {
|
||||||
|
// Always use new value except for videoQuotaDaily field
|
||||||
|
if (fieldName === 'videoQuotaDaily') return currentValue
|
||||||
|
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user