fix(xo-server-auth-ldap): create logger inside plugin (#5864)
The plugin was wrongly expecting a logger instance to be passed on instantiation
This commit is contained in:
parent
656dc8fefc
commit
d5f5cdd27a
@ -31,6 +31,7 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@xen-orchestra/log": "^0.2.1",
|
||||||
"exec-promise": "^0.7.0",
|
"exec-promise": "^0.7.0",
|
||||||
"inquirer": "^8.0.0",
|
"inquirer": "^8.0.0",
|
||||||
"ldapts": "^2.2.1",
|
"ldapts": "^2.2.1",
|
||||||
|
@ -3,9 +3,12 @@
|
|||||||
import ensureArray from 'ensure-array'
|
import ensureArray from 'ensure-array'
|
||||||
import fromCallback from 'promise-toolbox/fromCallback'
|
import fromCallback from 'promise-toolbox/fromCallback'
|
||||||
import { Client } from 'ldapts'
|
import { Client } from 'ldapts'
|
||||||
|
import { createLogger } from '@xen-orchestra/log'
|
||||||
import { Filter } from 'ldapts/filters/Filter'
|
import { Filter } from 'ldapts/filters/Filter'
|
||||||
import { readFile } from 'fs'
|
import { readFile } from 'fs'
|
||||||
|
|
||||||
|
const logger = createLogger('xo:xo-server-auth-ldap')
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
@ -27,8 +30,6 @@ const evalFilter = (filter, vars) =>
|
|||||||
return escape(value)
|
return escape(value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const noop = Function.prototype
|
|
||||||
|
|
||||||
export const configurationSchema = {
|
export const configurationSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@ -184,8 +185,7 @@ export const testSchema = {
|
|||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
class AuthLdap {
|
class AuthLdap {
|
||||||
constructor({ logger = noop, xo }) {
|
constructor({ xo } = {}) {
|
||||||
this._logger = logger
|
|
||||||
this._xo = xo
|
this._xo = xo
|
||||||
|
|
||||||
this._authenticate = this._authenticate.bind(this)
|
this._authenticate = this._authenticate.bind(this)
|
||||||
@ -257,10 +257,8 @@ class AuthLdap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _authenticate({ username, password }) {
|
async _authenticate({ username, password }) {
|
||||||
const logger = this._logger
|
|
||||||
|
|
||||||
if (username === undefined || password === undefined) {
|
if (username === undefined || password === undefined) {
|
||||||
logger('require `username` and `password` to authenticate!')
|
logger.debug('require `username` and `password` to authenticate!')
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -276,29 +274,34 @@ class AuthLdap {
|
|||||||
{
|
{
|
||||||
const { _credentials: credentials } = this
|
const { _credentials: credentials } = this
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
logger(`attempting to bind with as ${credentials.dn}...`)
|
logger.debug(`attempting to bind with as ${credentials.dn}...`)
|
||||||
await client.bind(credentials.dn, credentials.password)
|
await client.bind(credentials.dn, credentials.password)
|
||||||
logger(`successfully bound as ${credentials.dn}`)
|
logger.debug(`successfully bound as ${credentials.dn}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for the user.
|
// Search for the user.
|
||||||
logger('searching for entries...')
|
logger.debug('searching for entries...')
|
||||||
const { searchEntries: entries } = await client.search(this._searchBase, {
|
const { searchEntries: entries } = await client.search(this._searchBase, {
|
||||||
scope: 'sub',
|
scope: 'sub',
|
||||||
filter: evalFilter(this._searchFilter, {
|
filter: evalFilter(this._searchFilter, {
|
||||||
name: username,
|
name: username,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
logger(`${entries.length} entries found`)
|
logger.debug(`${entries.length} entries found`)
|
||||||
|
|
||||||
// Try to find an entry which can be bind with the given password.
|
// Try to find an entry which can be bind with the given password.
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
try {
|
try {
|
||||||
logger(`attempting to bind as ${entry.dn}`)
|
logger.debug(`attempting to bind as ${entry.dn}`)
|
||||||
await client.bind(entry.dn, password)
|
await client.bind(entry.dn, password)
|
||||||
logger(`successfully bound as ${entry.dn} => ${username} authenticated`)
|
logger.info(`successfully bound as ${entry.dn} => ${username} authenticated`)
|
||||||
logger(JSON.stringify(entry, null, 2))
|
logger.debug(JSON.stringify(entry, null, 2))
|
||||||
|
|
||||||
|
// CLI test: don't register user/sync groups
|
||||||
|
if (this._xo === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let user
|
let user
|
||||||
if (this._userIdAttribute === undefined) {
|
if (this._userIdAttribute === undefined) {
|
||||||
@ -315,18 +318,18 @@ class AuthLdap {
|
|||||||
try {
|
try {
|
||||||
await this._synchronizeGroups(user, entry[groupsConfig.membersMapping.userAttribute])
|
await this._synchronizeGroups(user, entry[groupsConfig.membersMapping.userAttribute])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger(`failed to synchronize groups: ${error.message}`)
|
logger.error(`failed to synchronize groups: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { userId: user.id }
|
return { userId: user.id }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger(`failed to bind as ${entry.dn}: ${error.message}`)
|
logger.debug(`failed to bind as ${entry.dn}: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger(`could not authenticate ${username}`)
|
logger.debug(`could not authenticate ${username}`)
|
||||||
return null
|
return null
|
||||||
} finally {
|
} finally {
|
||||||
await client.unbind()
|
await client.unbind()
|
||||||
@ -335,7 +338,6 @@ class AuthLdap {
|
|||||||
|
|
||||||
// Synchronize user's groups OR all groups if no user is passed
|
// Synchronize user's groups OR all groups if no user is passed
|
||||||
async _synchronizeGroups(user, memberId) {
|
async _synchronizeGroups(user, memberId) {
|
||||||
const logger = this._logger
|
|
||||||
const client = new Client(this._clientOpts)
|
const client = new Client(this._clientOpts)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -347,12 +349,12 @@ class AuthLdap {
|
|||||||
{
|
{
|
||||||
const { _credentials: credentials } = this
|
const { _credentials: credentials } = this
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
logger(`attempting to bind with as ${credentials.dn}...`)
|
logger.debug(`attempting to bind with as ${credentials.dn}...`)
|
||||||
await client.bind(credentials.dn, credentials.password)
|
await client.bind(credentials.dn, credentials.password)
|
||||||
logger(`successfully bound as ${credentials.dn}`)
|
logger.debug(`successfully bound as ${credentials.dn}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger('syncing groups...')
|
logger.info('syncing groups...')
|
||||||
const { base, displayNameAttribute, filter, idAttribute, membersMapping } = this._groupsConfig
|
const { base, displayNameAttribute, filter, idAttribute, membersMapping } = this._groupsConfig
|
||||||
const { searchEntries: ldapGroups } = await client.search(base, {
|
const { searchEntries: ldapGroups } = await client.search(base, {
|
||||||
scope: 'sub',
|
scope: 'sub',
|
||||||
@ -374,7 +376,7 @@ class AuthLdap {
|
|||||||
|
|
||||||
// Empty or undefined names/IDs are invalid
|
// Empty or undefined names/IDs are invalid
|
||||||
if (!groupLdapId || !groupLdapName) {
|
if (!groupLdapId || !groupLdapName) {
|
||||||
logger(`Invalid group ID (${groupLdapId}) or name (${groupLdapName})`)
|
logger.error(`Invalid group ID (${groupLdapId}) or name (${groupLdapName})`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +395,7 @@ class AuthLdap {
|
|||||||
if (xoGroupIndex === -1) {
|
if (xoGroupIndex === -1) {
|
||||||
if (xoGroups.find(group => group.name === groupLdapName) !== undefined) {
|
if (xoGroups.find(group => group.name === groupLdapName) !== undefined) {
|
||||||
// TODO: check against LDAP groups that are being created as well
|
// TODO: check against LDAP groups that are being created as well
|
||||||
logger(`A group called ${groupLdapName} already exists`)
|
logger.error(`A group called ${groupLdapName} already exists`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
xoGroup = await this._xo.createGroup({
|
xoGroup = await this._xo.createGroup({
|
||||||
@ -459,6 +461,8 @@ class AuthLdap {
|
|||||||
xoGroups.filter(group => group.provider === 'ldap').map(group => this._xo.deleteGroup(group.id))
|
xoGroups.filter(group => group.provider === 'ldap').map(group => this._xo.deleteGroup(group.id))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info('done syncing groups')
|
||||||
} finally {
|
} finally {
|
||||||
await client.unbind()
|
await client.unbind()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import execPromise from 'exec-promise'
|
import execPromise from 'exec-promise'
|
||||||
|
import transportConsole from '@xen-orchestra/log/transports/console'
|
||||||
|
import { configure } from '@xen-orchestra/log/configure.js'
|
||||||
import { fromCallback } from 'promise-toolbox'
|
import { fromCallback } from 'promise-toolbox'
|
||||||
import { readFile, writeFile } from 'fs'
|
import { readFile, writeFile } from 'fs'
|
||||||
|
|
||||||
@ -28,9 +30,14 @@ execPromise(async args => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const plugin = createPlugin({
|
configure([
|
||||||
logger: console.log.bind(console),
|
{
|
||||||
})
|
filter: process.env.DEBUG ?? 'xo:xo-server-auth-ldap',
|
||||||
|
transport: transportConsole(),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const plugin = createPlugin()
|
||||||
await plugin.configure(config)
|
await plugin.configure(config)
|
||||||
|
|
||||||
await plugin._authenticate({
|
await plugin._authenticate({
|
||||||
|
Loading…
Reference in New Issue
Block a user